nicot 1.1.22 → 1.1.24

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 (71) hide show
  1. package/README.md +2 -0
  2. package/build.js +167 -0
  3. package/dist/index.cjs +2210 -0
  4. package/dist/index.cjs.map +7 -0
  5. package/dist/index.d.ts +0 -1
  6. package/dist/index.mjs +2184 -0
  7. package/dist/index.mjs.map +7 -0
  8. package/index.ts +0 -2
  9. package/package.json +20 -7
  10. package/dist/index.js +0 -25
  11. package/dist/index.js.map +0 -1
  12. package/dist/src/bases/base-restful-controller.js +0 -53
  13. package/dist/src/bases/base-restful-controller.js.map +0 -1
  14. package/dist/src/bases/id-base.js +0 -58
  15. package/dist/src/bases/id-base.js.map +0 -1
  16. package/dist/src/bases/index.js +0 -20
  17. package/dist/src/bases/index.js.map +0 -1
  18. package/dist/src/bases/page-settings.js +0 -64
  19. package/dist/src/bases/page-settings.js.map +0 -1
  20. package/dist/src/bases/time-base.js +0 -53
  21. package/dist/src/bases/time-base.js.map +0 -1
  22. package/dist/src/crud-base.js +0 -480
  23. package/dist/src/crud-base.js.map +0 -1
  24. package/dist/src/decorators/access.js +0 -24
  25. package/dist/src/decorators/access.js.map +0 -1
  26. package/dist/src/decorators/index.js +0 -21
  27. package/dist/src/decorators/index.js.map +0 -1
  28. package/dist/src/decorators/pipes.js +0 -26
  29. package/dist/src/decorators/pipes.js.map +0 -1
  30. package/dist/src/decorators/property.js +0 -191
  31. package/dist/src/decorators/property.js.map +0 -1
  32. package/dist/src/decorators/query.js +0 -67
  33. package/dist/src/decorators/query.js.map +0 -1
  34. package/dist/src/dto/cursor-pagination.js +0 -89
  35. package/dist/src/dto/cursor-pagination.js.map +0 -1
  36. package/dist/src/dto/import-entry.js +0 -45
  37. package/dist/src/dto/import-entry.js.map +0 -1
  38. package/dist/src/dto/index.js +0 -19
  39. package/dist/src/dto/index.js.map +0 -1
  40. package/dist/src/restful.js +0 -402
  41. package/dist/src/restful.js.map +0 -1
  42. package/dist/src/utility/bigint.js +0 -16
  43. package/dist/src/utility/bigint.js.map +0 -1
  44. package/dist/src/utility/cursor-pagination-utils.js +0 -252
  45. package/dist/src/utility/cursor-pagination-utils.js.map +0 -1
  46. package/dist/src/utility/filter-relations.js +0 -91
  47. package/dist/src/utility/filter-relations.js.map +0 -1
  48. package/dist/src/utility/get-typeorm-relations.js +0 -50
  49. package/dist/src/utility/get-typeorm-relations.js.map +0 -1
  50. package/dist/src/utility/index.js +0 -18
  51. package/dist/src/utility/index.js.map +0 -1
  52. package/dist/src/utility/metadata.js +0 -21
  53. package/dist/src/utility/metadata.js.map +0 -1
  54. package/dist/src/utility/omit-type-exclude.js +0 -14
  55. package/dist/src/utility/omit-type-exclude.js.map +0 -1
  56. package/dist/src/utility/query-full-text-column-options.interface.js +0 -3
  57. package/dist/src/utility/query-full-text-column-options.interface.js.map +0 -1
  58. package/dist/src/utility/query.js +0 -49
  59. package/dist/src/utility/query.js.map +0 -1
  60. package/dist/src/utility/recursive-key-of.js +0 -4
  61. package/dist/src/utility/recursive-key-of.js.map +0 -1
  62. package/dist/src/utility/relation-def.js +0 -3
  63. package/dist/src/utility/relation-def.js.map +0 -1
  64. package/dist/src/utility/rename-class.js +0 -8
  65. package/dist/src/utility/rename-class.js.map +0 -1
  66. package/dist/src/utility/subject-registry.js +0 -19
  67. package/dist/src/utility/subject-registry.js.map +0 -1
  68. package/dist/src/utility/type-transformer.js +0 -22
  69. package/dist/src/utility/type-transformer.js.map +0 -1
  70. package/dist/src/utility/unshift-order-by.js +0 -18
  71. package/dist/src/utility/unshift-order-by.js.map +0 -1
package/dist/index.mjs ADDED
@@ -0,0 +1,2184 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result) __defProp(target, key, result);
9
+ return result;
10
+ };
11
+
12
+ // index.ts
13
+ export * from "nesties";
14
+
15
+ // src/dto/import-entry.ts
16
+ import { ApiProperty } from "@nestjs/swagger";
17
+ import { Type } from "class-transformer";
18
+ import { ValidateNested } from "class-validator";
19
+ import {
20
+ getClassFromClassOrArray,
21
+ InsertField
22
+ } from "nesties";
23
+ var ImportEntryBaseDto = class {
24
+ };
25
+ __decorateClass([
26
+ ApiProperty({ description: "Import result", type: String })
27
+ ], ImportEntryBaseDto.prototype, "result", 2);
28
+ function ImportEntryDto(type) {
29
+ return InsertField(
30
+ ImportEntryBaseDto,
31
+ {
32
+ entry: { type, options: { description: "Import entry" } }
33
+ },
34
+ `${getClassFromClassOrArray(type).name}ImportEntry`
35
+ );
36
+ }
37
+ var ImportDataBaseDto = class {
38
+ };
39
+ __decorateClass([
40
+ ValidateNested()
41
+ ], ImportDataBaseDto.prototype, "data", 2);
42
+ function ImportDataDto(type) {
43
+ const dtoClass = InsertField(
44
+ ImportDataBaseDto,
45
+ {
46
+ data: { type: [type], options: { description: "Import data" } }
47
+ },
48
+ `${getClassFromClassOrArray(type).name}ImportData`
49
+ );
50
+ Type(() => type)(dtoClass.prototype, "data");
51
+ return dtoClass;
52
+ }
53
+
54
+ // src/dto/cursor-pagination.ts
55
+ import {
56
+ IsInt as IsInt2,
57
+ IsNotEmpty,
58
+ IsOptional as IsOptional3,
59
+ IsPositive,
60
+ IsString as IsString2
61
+ } from "class-validator";
62
+ import { ApiProperty as ApiProperty3 } from "@nestjs/swagger";
63
+ import {
64
+ BlankReturnMessageDto,
65
+ getClassFromClassOrArray as getClassFromClassOrArray3,
66
+ InsertField as InsertField2
67
+ } from "nesties";
68
+
69
+ // src/decorators/access.ts
70
+ import { Expose } from "class-transformer";
71
+ import { IsOptional } from "class-validator";
72
+
73
+ // src/utility/metadata.ts
74
+ import { MetadataSetter, Reflector } from "typed-reflector";
75
+ var Metadata = new MetadataSetter();
76
+ var reflector = new Reflector();
77
+ function getSpecificFields(obj, type, filter = () => true) {
78
+ return reflector.getArray(`${type}Fields`, obj).filter((field) => {
79
+ const value = reflector.get(type, obj, field);
80
+ if (value == null) {
81
+ return false;
82
+ }
83
+ return filter(value, obj);
84
+ });
85
+ }
86
+ function getNotInResultFields(obj, keepEntityVersioningDates = false) {
87
+ return getSpecificFields(
88
+ obj,
89
+ "notInResult",
90
+ (meta) => !keepEntityVersioningDates || !meta.entityVersioningDate
91
+ );
92
+ }
93
+
94
+ // src/decorators/access.ts
95
+ import { MergePropertyDecorators } from "nesties";
96
+ var NotWritable = () => MergePropertyDecorators([
97
+ Expose({ groups: ["r"] }),
98
+ IsOptional(),
99
+ Metadata.set("notWritable", true, "notWritableFields"),
100
+ Metadata.set("notChangeable", true, "notChangeableFields")
101
+ ]);
102
+ var NotChangeable = () => MergePropertyDecorators([
103
+ Expose({ groups: ["r", "c"] }),
104
+ Metadata.set("notChangeable", true, "notChangeableFields")
105
+ ]);
106
+ var NotQueryable = () => Metadata.set("notQueryable", true, "notQueryableFields");
107
+ var NotInResult = (options = {}) => Metadata.set("notInResult", options, "notInResultFields");
108
+
109
+ // src/decorators/property.ts
110
+ import { ApiProperty as ApiProperty2 } from "@nestjs/swagger";
111
+ import { MergePropertyDecorators as MergePropertyDecorators2 } from "nesties";
112
+ import { Column, Index } from "typeorm";
113
+ import {
114
+ IsDate,
115
+ IsEnum,
116
+ IsInt,
117
+ IsNumber,
118
+ IsOptional as IsOptional2,
119
+ IsString,
120
+ MaxLength,
121
+ Min,
122
+ ValidateNested as ValidateNested2
123
+ } from "class-validator";
124
+ import { Exclude, Transform, Type as Type2 } from "class-transformer";
125
+
126
+ // src/utility/bigint.ts
127
+ var BigintTransformer = class {
128
+ from(dbValue) {
129
+ if (dbValue == null) {
130
+ return dbValue;
131
+ }
132
+ return parseInt(dbValue);
133
+ }
134
+ to(entValue) {
135
+ return entValue;
136
+ }
137
+ };
138
+
139
+ // src/decorators/property.ts
140
+ import { getClassFromClassOrArray as getClassFromClassOrArray2 } from "nesties";
141
+
142
+ // src/utility/type-transformer.ts
143
+ var TypeTransformer = class {
144
+ constructor(definition) {
145
+ this.definition = definition;
146
+ }
147
+ from(dbValue) {
148
+ if (!dbValue) {
149
+ return dbValue;
150
+ }
151
+ if (Array.isArray(this.definition)) {
152
+ return dbValue.map(
153
+ (value) => Object.assign(new this.definition[0](), value)
154
+ );
155
+ }
156
+ return Object.assign(new this.definition(), dbValue);
157
+ }
158
+ to(entValue) {
159
+ return entValue;
160
+ }
161
+ };
162
+
163
+ // src/decorators/property.ts
164
+ function swaggerDecorator(options, injected = {}) {
165
+ return ApiProperty2({
166
+ default: options.default,
167
+ required: !!(options.required && options.default == null),
168
+ example: options.default,
169
+ description: options.description,
170
+ ...injected,
171
+ ...options.propertyExtras || {}
172
+ });
173
+ }
174
+ function validatorDecorator(options) {
175
+ const decs = [];
176
+ if (!options.required) {
177
+ decs.push(IsOptional2());
178
+ }
179
+ return MergePropertyDecorators2(decs);
180
+ }
181
+ function columnDecoratorOptions(options) {
182
+ return {
183
+ default: options.default,
184
+ nullable: !options.required && options.default == null,
185
+ comment: options.description,
186
+ ...options.columnExtras
187
+ };
188
+ }
189
+ var StringColumn = (length, options = {}) => {
190
+ return MergePropertyDecorators2([
191
+ Column("varchar", { length, ...columnDecoratorOptions(options) }),
192
+ IsString(),
193
+ MaxLength(length),
194
+ validatorDecorator(options),
195
+ swaggerDecorator(options, { type: String, maxLength: length })
196
+ ]);
197
+ };
198
+ var IntColumn = (type, options = {}) => {
199
+ const decs = [
200
+ Column(type, {
201
+ default: options.default,
202
+ unsigned: options.unsigned,
203
+ ...type === "bigint" ? { transformer: new BigintTransformer() } : {},
204
+ ...columnDecoratorOptions(options)
205
+ }),
206
+ IsInt(),
207
+ validatorDecorator(options),
208
+ swaggerDecorator(options, {
209
+ type: Number,
210
+ minimum: options.unsigned ? 0 : void 0
211
+ })
212
+ ];
213
+ if (options.unsigned) {
214
+ decs.push(Min(0));
215
+ }
216
+ return MergePropertyDecorators2(decs);
217
+ };
218
+ var FloatColumn = (type, options = {}) => {
219
+ const decs = [
220
+ Column(type, {
221
+ default: options.default,
222
+ unsigned: options.unsigned,
223
+ ...columnDecoratorOptions(options)
224
+ }),
225
+ IsNumber(),
226
+ validatorDecorator(options),
227
+ swaggerDecorator(options, {
228
+ type: Number,
229
+ minimum: options.unsigned ? 0 : void 0
230
+ })
231
+ ];
232
+ if (options.unsigned) {
233
+ decs.push(Min(0));
234
+ }
235
+ return MergePropertyDecorators2(decs);
236
+ };
237
+ var DateColumn = (options = {}) => {
238
+ return MergePropertyDecorators2([
239
+ Column("timestamp", columnDecoratorOptions(options)),
240
+ IsDate(),
241
+ Transform(
242
+ (v) => {
243
+ const value = v.value;
244
+ if (value == null || value instanceof Date) return value;
245
+ const timestampToDate = (t, isSeconds) => new Date(isSeconds ? t * 1e3 : t);
246
+ if (typeof value === "number") {
247
+ const isSeconds = !Number.isInteger(value) || value < 1e12;
248
+ return timestampToDate(value, isSeconds);
249
+ }
250
+ if (typeof value === "string" && /^\d+(\.\d+)?$/.test(value)) {
251
+ const isSeconds = value.includes(".") || parseFloat(value) < 1e12;
252
+ return timestampToDate(parseFloat(value), isSeconds);
253
+ }
254
+ return new Date(value);
255
+ },
256
+ {
257
+ toClassOnly: true
258
+ }
259
+ ),
260
+ validatorDecorator(options),
261
+ swaggerDecorator(options, { type: Date })
262
+ ]);
263
+ };
264
+ var EnumColumn = (targetEnum, options = {}) => {
265
+ return MergePropertyDecorators2([
266
+ Index(),
267
+ Column("enum", {
268
+ enum: targetEnum,
269
+ ...columnDecoratorOptions(options)
270
+ }),
271
+ IsEnum(targetEnum),
272
+ validatorDecorator(options),
273
+ swaggerDecorator(options, { enum: targetEnum })
274
+ ]);
275
+ };
276
+ var BoolColumn = (options = {}) => MergePropertyDecorators2([
277
+ Index(),
278
+ Transform((v) => {
279
+ const trueValues = ["true", "1", "yes", "on", true, 1];
280
+ const falseValues = ["false", "0", "no", "off", false, 0];
281
+ if (trueValues.indexOf(v.value) !== -1) return true;
282
+ if (falseValues.indexOf(v.value) !== -1) return false;
283
+ return void 0;
284
+ }),
285
+ Column("boolean", columnDecoratorOptions(options)),
286
+ validatorDecorator(options),
287
+ swaggerDecorator(options, { type: Boolean })
288
+ ]);
289
+ var JsonColumn = (definition, options = {}) => {
290
+ const cl = getClassFromClassOrArray2(definition);
291
+ return MergePropertyDecorators2([
292
+ NotQueryable(),
293
+ Type2(() => cl),
294
+ ValidateNested2(),
295
+ Column("jsonb", {
296
+ ...columnDecoratorOptions(options),
297
+ transformer: new TypeTransformer(definition)
298
+ }),
299
+ validatorDecorator(options),
300
+ swaggerDecorator(options, { type: definition })
301
+ ]);
302
+ };
303
+ var NotColumn = (options = {}, specials = {}) => MergePropertyDecorators2([
304
+ Exclude(),
305
+ swaggerDecorator({
306
+ required: false,
307
+ ...options
308
+ }),
309
+ Metadata.set("notColumn", specials, "notColumnFields")
310
+ ]);
311
+ var QueryColumn = (options = {}) => MergePropertyDecorators2([
312
+ NotWritable(),
313
+ NotInResult(),
314
+ swaggerDecorator({
315
+ required: false,
316
+ ...options
317
+ })
318
+ ]);
319
+ var RelationComputed = (type) => (obj, propertyKey) => {
320
+ const fun = () => {
321
+ const designType = Reflect.getMetadata("design:type", obj, propertyKey);
322
+ const entityClass = type ? type() : designType;
323
+ return {
324
+ entityClass,
325
+ isArray: designType === Array
326
+ };
327
+ };
328
+ const dec = Metadata.set("relationComputed", fun, "relationComputedFields");
329
+ return dec(obj, propertyKey);
330
+ };
331
+
332
+ // src/decorators/pipes.ts
333
+ import { ValidationPipe } from "@nestjs/common";
334
+ var CreatePipe = () => new ValidationPipe({
335
+ transform: true,
336
+ transformOptions: { groups: ["c"], enableImplicitConversion: true }
337
+ });
338
+ var GetPipe = () => new ValidationPipe({
339
+ transform: true,
340
+ transformOptions: { groups: ["r"], enableImplicitConversion: true },
341
+ skipMissingProperties: true,
342
+ skipNullProperties: true,
343
+ skipUndefinedProperties: true
344
+ });
345
+ var UpdatePipe = () => new ValidationPipe({
346
+ transform: true,
347
+ transformOptions: { groups: ["u"], enableImplicitConversion: true },
348
+ skipMissingProperties: true,
349
+ skipNullProperties: true,
350
+ skipUndefinedProperties: true
351
+ });
352
+
353
+ // src/utility/query.ts
354
+ function createQueryCondition(cond) {
355
+ return (obj, qb, entityName, ...fields) => {
356
+ for (const field of fields) {
357
+ if (obj[field] == null) {
358
+ continue;
359
+ }
360
+ const ret = cond(obj, qb, entityName, field);
361
+ if (typeof ret === "string") {
362
+ qb.andWhere(ret);
363
+ } else if (typeof ret === "object" && typeof ret["query"] === "string") {
364
+ const _ret = ret;
365
+ qb.andWhere(_ret.query, _ret.params);
366
+ }
367
+ }
368
+ return qb;
369
+ };
370
+ }
371
+ var applyQueryProperty = createQueryCondition(
372
+ (obj, qb, entityName, field) => qb.andWhere(`${entityName}.${field} = :${field}`, { [field]: obj[field] })
373
+ );
374
+ var applyQueryPropertyLike = createQueryCondition(
375
+ (obj, qb, entityName, field) => qb.andWhere(`${entityName}.${field} like (:${field} || '%')`, {
376
+ [field]: obj[field]
377
+ })
378
+ );
379
+ var applyQueryPropertySearch = createQueryCondition(
380
+ (obj, qb, entityName, field) => qb.andWhere(`${entityName}.${field} like ('%' || :${field} || '%')`, {
381
+ [field]: obj[field]
382
+ })
383
+ );
384
+ var applyQueryPropertyZeroNullable = createQueryCondition(
385
+ (obj, qb, entityName, field) => {
386
+ if ([0, "0"].indexOf(obj[field]) !== -1) {
387
+ qb.andWhere(`${entityName}.${field} IS NULL`);
388
+ } else {
389
+ qb.andWhere(`${entityName}.${field} = :${field}`, {
390
+ [field]: obj[field]
391
+ });
392
+ }
393
+ }
394
+ );
395
+ var applyQueryMatchBoolean = createQueryCondition(
396
+ (obj, qb, entityName, field) => {
397
+ const value = obj[field];
398
+ if (value === true || value === "true" || value === 1 || value === "1") {
399
+ qb.andWhere(`${entityName}.${field} = TRUE`);
400
+ }
401
+ if (value === false || value === "false" || value === 0 || value === "0") {
402
+ qb.andWhere(`${entityName}.${field} = FALSE`);
403
+ }
404
+ }
405
+ );
406
+
407
+ // src/decorators/query.ts
408
+ import { MergePropertyDecorators as MergePropertyDecorators3 } from "nesties";
409
+
410
+ // src/utility/unshift-order-by.ts
411
+ var unshiftOrderBy = (qb, sort, order, nulls) => {
412
+ const currentOrderBys = Object.entries(qb.expressionMap.allOrderBys);
413
+ qb.orderBy(sort, order, nulls);
414
+ currentOrderBys.forEach(([key, value]) => {
415
+ if (typeof value === "string") {
416
+ qb.addOrderBy(key, value);
417
+ } else {
418
+ qb.addOrderBy(key, value.order, value.nulls);
419
+ }
420
+ });
421
+ return qb;
422
+ };
423
+
424
+ // src/utility/subject-registry.ts
425
+ var subjectRegistry = /* @__PURE__ */ new WeakMap();
426
+ var addSubject = (qb, select, alias) => {
427
+ let subjects = subjectRegistry.get(qb);
428
+ if (!subjects) {
429
+ subjects = {};
430
+ subjectRegistry.set(qb, subjects);
431
+ }
432
+ subjects[alias] = select;
433
+ return qb.addSelect(select, alias);
434
+ };
435
+ var getSubject = (qb, alias) => {
436
+ return subjectRegistry.get(qb)?.[alias];
437
+ };
438
+
439
+ // src/decorators/query.ts
440
+ var QueryCondition = (cond) => Metadata.set(
441
+ "queryCondition",
442
+ cond,
443
+ "queryConditionFields"
444
+ );
445
+ var QueryEqual = () => QueryCondition(applyQueryProperty);
446
+ var QueryLike = () => QueryCondition(applyQueryPropertyLike);
447
+ var QuerySearch = () => QueryCondition(applyQueryPropertySearch);
448
+ var QueryEqualZeroNullable = () => QueryCondition(applyQueryPropertyZeroNullable);
449
+ var QueryMatchBoolean = () => QueryCondition(applyQueryMatchBoolean);
450
+ var QueryOperator = (operator, field) => QueryCondition((obj, qb, entityName, key) => {
451
+ if (obj[key] == null) return;
452
+ const fieldName = field || key;
453
+ const typeormField = `_query_operator_${entityName}_${fieldName}_${key}`;
454
+ qb.andWhere(`${entityName}.${fieldName} ${operator} :${typeormField}`, {
455
+ [typeormField]: obj[key]
456
+ });
457
+ });
458
+ var createQueryOperator = (operator) => (field) => QueryOperator(operator, field);
459
+ var QueryGreater = createQueryOperator(">");
460
+ var QueryGreaterEqual = createQueryOperator(">=");
461
+ var QueryLess = createQueryOperator("<");
462
+ var QueryLessEqual = createQueryOperator("<=");
463
+ var QueryNotEqual = createQueryOperator("!=");
464
+ var QueryFullText = (options = {}) => {
465
+ const configurationName = options.parser ? `nicot_parser_${options.parser}` : options.configuration || "english";
466
+ const tsQueryFunction = options.tsQueryFunction || "websearch_to_tsquery";
467
+ return MergePropertyDecorators3([
468
+ QueryCondition((obj, qb, entityName, key) => {
469
+ if (obj[key] == null) return;
470
+ const fieldName = key;
471
+ const typeormField = key;
472
+ const tsVectorStatement = `to_tsvector('${configurationName}', "${entityName}"."${fieldName}")`;
473
+ const tsQueryStatement = `${tsQueryFunction}('${configurationName}', :${typeormField})`;
474
+ qb.andWhere(`${tsVectorStatement} @@ ${tsQueryStatement}`, {
475
+ [typeormField]: obj[key]
476
+ });
477
+ if (options.orderBySimilarity) {
478
+ const rankVirtualField = `_fulltext_rank_${key}`;
479
+ addSubject(
480
+ qb,
481
+ `ts_rank(${tsVectorStatement}, ${tsQueryStatement})`,
482
+ rankVirtualField
483
+ );
484
+ unshiftOrderBy(qb, `"${rankVirtualField}"`, "DESC");
485
+ }
486
+ }),
487
+ Metadata.set(
488
+ "queryFullTextColumn",
489
+ {
490
+ ...options,
491
+ configuration: configurationName
492
+ },
493
+ "queryFullTextColumnFields"
494
+ )
495
+ ]);
496
+ };
497
+
498
+ // src/dto/cursor-pagination.ts
499
+ var CursorPaginationDto = class {
500
+ };
501
+ __decorateClass([
502
+ NotWritable(),
503
+ IsNotEmpty(),
504
+ IsString2(),
505
+ IsOptional3(),
506
+ ApiProperty3({
507
+ description: "Pagination Cursor",
508
+ required: false,
509
+ type: String
510
+ }),
511
+ NotInResult()
512
+ ], CursorPaginationDto.prototype, "paginationCursor", 2);
513
+ __decorateClass([
514
+ NotWritable(),
515
+ IsPositive(),
516
+ IsInt2(),
517
+ ApiProperty3({
518
+ description: "Records per page.",
519
+ required: false,
520
+ type: Number,
521
+ minimum: 1
522
+ }),
523
+ NotInResult()
524
+ ], CursorPaginationDto.prototype, "recordsPerPage", 2);
525
+ var BlankCursorPaginationReturnMessageDto = class extends BlankReturnMessageDto {
526
+ constructor(statusCode, message, cursorResponse) {
527
+ super(statusCode, message);
528
+ this.nextCursor = cursorResponse.nextCursor;
529
+ this.previousCursor = cursorResponse.previousCursor;
530
+ }
531
+ };
532
+ __decorateClass([
533
+ ApiProperty3({
534
+ description: "Next Cursor",
535
+ required: false,
536
+ type: String
537
+ })
538
+ ], BlankCursorPaginationReturnMessageDto.prototype, "nextCursor", 2);
539
+ __decorateClass([
540
+ ApiProperty3({
541
+ description: "Previous Cursor",
542
+ required: false,
543
+ type: String
544
+ })
545
+ ], BlankCursorPaginationReturnMessageDto.prototype, "previousCursor", 2);
546
+ var GenericCursorPaginationReturnMessageDto = class extends BlankCursorPaginationReturnMessageDto {
547
+ constructor(statusCode, message, data, cursorResponse) {
548
+ super(statusCode, message, cursorResponse);
549
+ this.data = data;
550
+ }
551
+ };
552
+ function CursorPaginationReturnMessageDto(type) {
553
+ return InsertField2(
554
+ GenericCursorPaginationReturnMessageDto,
555
+ {
556
+ data: {
557
+ type: [type],
558
+ options: {
559
+ required: false,
560
+ description: "Return data."
561
+ }
562
+ }
563
+ },
564
+ `${getClassFromClassOrArray3(type).name}CursorPaginationReturnMessageDto`
565
+ );
566
+ }
567
+
568
+ // src/crud-base.ts
569
+ import {
570
+ In
571
+ } from "typeorm";
572
+
573
+ // src/bases/time-base.ts
574
+ import {
575
+ CreateDateColumn,
576
+ DeleteDateColumn,
577
+ UpdateDateColumn
578
+ } from "typeorm";
579
+
580
+ // src/bases/page-settings.ts
581
+ import { IsInt as IsInt3, IsPositive as IsPositive2 } from "class-validator";
582
+ import { ApiProperty as ApiProperty4 } from "@nestjs/swagger";
583
+ var PageSettingsDto = class {
584
+ getActualPageSettings() {
585
+ return {
586
+ pageCount: this.getPageCount(),
587
+ recordsPerPage: this.getRecordsPerPage()
588
+ };
589
+ }
590
+ getPageCount() {
591
+ return parseInt(this.pageCount) || 1;
592
+ }
593
+ getRecordsPerPage() {
594
+ return parseInt(this.recordsPerPage) || 25;
595
+ }
596
+ getStartingFrom() {
597
+ return (this.getPageCount() - 1) * this.getRecordsPerPage();
598
+ }
599
+ applyPaginationQuery(qb) {
600
+ qb.take(this.getRecordsPerPage()).skip(this.getStartingFrom());
601
+ }
602
+ applyQuery(qb, entityName) {
603
+ }
604
+ };
605
+ __decorateClass([
606
+ NotWritable(),
607
+ IsPositive2(),
608
+ IsInt3(),
609
+ ApiProperty4({
610
+ description: "The nth page, starting with 1.",
611
+ required: false,
612
+ type: Number,
613
+ minimum: 1
614
+ }),
615
+ NotInResult()
616
+ ], PageSettingsDto.prototype, "pageCount", 2);
617
+ __decorateClass([
618
+ NotWritable(),
619
+ IsPositive2(),
620
+ IsInt3(),
621
+ ApiProperty4({
622
+ description: "Records per page.",
623
+ required: false,
624
+ type: Number,
625
+ minimum: 1
626
+ }),
627
+ NotInResult()
628
+ ], PageSettingsDto.prototype, "recordsPerPage", 2);
629
+
630
+ // src/bases/time-base.ts
631
+ var TimeBase = class extends PageSettingsDto {
632
+ isValidInCreate() {
633
+ return;
634
+ }
635
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
636
+ async beforeCreate() {
637
+ }
638
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
639
+ async afterCreate() {
640
+ }
641
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
642
+ async beforeGet() {
643
+ }
644
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
645
+ async afterGet() {
646
+ }
647
+ isValidInUpdate() {
648
+ return;
649
+ }
650
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
651
+ async beforeUpdate() {
652
+ }
653
+ };
654
+ __decorateClass([
655
+ CreateDateColumn({ select: false }),
656
+ NotColumn(),
657
+ NotInResult({ entityVersioningDate: true })
658
+ ], TimeBase.prototype, "createTime", 2);
659
+ __decorateClass([
660
+ UpdateDateColumn({ select: false }),
661
+ NotColumn(),
662
+ NotInResult({ entityVersioningDate: true })
663
+ ], TimeBase.prototype, "updateTime", 2);
664
+ __decorateClass([
665
+ DeleteDateColumn({ select: false }),
666
+ NotColumn(),
667
+ NotInResult({ entityVersioningDate: true })
668
+ ], TimeBase.prototype, "deleteTime", 2);
669
+
670
+ // src/bases/id-base.ts
671
+ import { Generated } from "typeorm";
672
+ import { IsNotEmpty as IsNotEmpty2, IsString as IsString3 } from "class-validator";
673
+ import { MergePropertyDecorators as MergePropertyDecorators4 } from "nesties";
674
+ function IdBase(idOptions = {}) {
675
+ const cl = class IdBase extends TimeBase {
676
+ applyQuery(qb, entityName) {
677
+ super.applyQuery(qb, entityName);
678
+ if (!idOptions.noOrderById) {
679
+ qb.orderBy(`${entityName}.id`, "DESC");
680
+ }
681
+ applyQueryProperty(this, qb, entityName, "id");
682
+ }
683
+ };
684
+ const dec = MergePropertyDecorators4([
685
+ NotWritable(),
686
+ IntColumn("bigint", {
687
+ unsigned: true,
688
+ description: idOptions.description,
689
+ columnExtras: { nullable: false, primary: true }
690
+ }),
691
+ Reflect.metadata("design:type", Number),
692
+ Generated("increment")
693
+ ]);
694
+ dec(cl.prototype, "id");
695
+ return cl;
696
+ }
697
+ function StringIdBase(idOptions) {
698
+ const cl = class StringIdBase extends TimeBase {
699
+ applyQuery(qb, entityName) {
700
+ super.applyQuery(qb, entityName);
701
+ console.log("idbase order by");
702
+ qb.orderBy(`${entityName}.id`, "ASC");
703
+ applyQueryProperty(this, qb, entityName, "id");
704
+ }
705
+ };
706
+ const decs = [
707
+ StringColumn(idOptions.length || (idOptions.uuid ? 36 : 255), {
708
+ required: !idOptions.uuid,
709
+ description: idOptions.description,
710
+ columnExtras: { primary: true, nullable: false }
711
+ }),
712
+ Reflect.metadata("design:type", String),
713
+ ...idOptions.uuid ? [Generated("uuid"), NotWritable()] : [IsString3(), IsNotEmpty2(), NotChangeable()]
714
+ ];
715
+ const dec = MergePropertyDecorators4(decs);
716
+ dec(cl.prototype, "id");
717
+ return cl;
718
+ }
719
+
720
+ // src/crud-base.ts
721
+ import { ConsoleLogger } from "@nestjs/common";
722
+ import { camelCase } from "typeorm/util/StringUtils";
723
+ import _3, { omit } from "lodash";
724
+ import {
725
+ BlankReturnMessageDto as BlankReturnMessageDto2,
726
+ PaginatedReturnMessageDto,
727
+ ReturnMessageDto
728
+ } from "nesties";
729
+
730
+ // src/utility/get-typeorm-relations.ts
731
+ import { getMetadataArgsStorage } from "typeorm";
732
+ import _ from "lodash";
733
+ function getTypeormRelations(cl) {
734
+ const relations = getMetadataArgsStorage().relations.filter(
735
+ (r) => r.target === cl
736
+ );
737
+ const typeormRelations = relations.map((relation) => {
738
+ const isArray = relation.relationType.endsWith("-many");
739
+ const relationClassFactory = relation.type;
740
+ let propertyClass;
741
+ if (typeof relationClassFactory === "function") {
742
+ const relationClass = relationClassFactory();
743
+ if (typeof relationClass === "function") {
744
+ propertyClass = relationClass;
745
+ }
746
+ }
747
+ if (!propertyClass) {
748
+ propertyClass = Reflect.getMetadata(
749
+ "design:type",
750
+ cl.prototype,
751
+ relation.propertyName
752
+ );
753
+ }
754
+ return {
755
+ isArray,
756
+ propertyClass,
757
+ propertyName: relation.propertyName,
758
+ computed: false
759
+ };
760
+ });
761
+ const computedRelations = getSpecificFields(cl, "relationComputed").map(
762
+ (field) => {
763
+ const meta = reflector.get("relationComputed", cl, field);
764
+ const res = meta();
765
+ return {
766
+ isArray: res.isArray,
767
+ propertyClass: res.entityClass,
768
+ propertyName: field,
769
+ computed: true
770
+ };
771
+ }
772
+ );
773
+ return _.uniqBy(
774
+ [...typeormRelations, ...computedRelations],
775
+ // Merge typeorm relations and computed relations
776
+ (r) => r.propertyName
777
+ );
778
+ }
779
+
780
+ // src/utility/cursor-pagination-utils.ts
781
+ import { Brackets } from "typeorm";
782
+ import _2 from "lodash";
783
+ import SJSON from "superjson";
784
+
785
+ // src/utility/filter-relations.ts
786
+ var extractRelationName = (relation) => {
787
+ if (typeof relation === "string") {
788
+ return relation;
789
+ } else {
790
+ return relation.name;
791
+ }
792
+ };
793
+ var typeormRelationsCache = /* @__PURE__ */ new Map();
794
+ var fetchTypeormRelations = (cl) => {
795
+ if (typeormRelationsCache.has(cl)) {
796
+ return typeormRelationsCache.get(cl);
797
+ }
798
+ const relations = getTypeormRelations(cl);
799
+ const map = Object.fromEntries(
800
+ relations.map((r) => [r.propertyName, r])
801
+ );
802
+ typeormRelationsCache.set(cl, map);
803
+ return map;
804
+ };
805
+ var filterRelations = (cl, relations, cond = () => true) => {
806
+ return relations?.filter((r) => {
807
+ const relationName = extractRelationName(r);
808
+ const checkLevel = (entityClass, name) => {
809
+ const [currentLevel, ...nextLevel] = name.split(".");
810
+ const relation = fetchTypeormRelations(entityClass)?.[currentLevel];
811
+ if (!relation) {
812
+ throw new Error(
813
+ `Relation ${currentLevel} not found in ${entityClass.name} (Reading ${relationName} in ${cl.name})`
814
+ );
815
+ }
816
+ if (relation.computed) return false;
817
+ if (!nextLevel.length) return true;
818
+ return checkLevel(relation.propertyClass, nextLevel.join("."));
819
+ };
820
+ return checkLevel(cl, relationName);
821
+ }) || [];
822
+ };
823
+ var queryColumnOptionsFromAlias = (qb, cl, rootAlias, alias) => {
824
+ const [field, key] = alias.split(".");
825
+ if (field === rootAlias) {
826
+ return {
827
+ relations: [],
828
+ column: qb.connection.getMetadata(cl)?.findColumnWithPropertyName(key)
829
+ };
830
+ }
831
+ const relationLevels = field.split("_");
832
+ let currentClass = cl;
833
+ const relations = [];
834
+ while (relationLevels.length) {
835
+ const f = relationLevels.shift();
836
+ const relation = fetchTypeormRelations(currentClass)?.[f];
837
+ if (!relation || relation.computed) {
838
+ return;
839
+ }
840
+ relations.push(relation);
841
+ currentClass = relation.propertyClass;
842
+ }
843
+ return {
844
+ relations,
845
+ column: qb.connection.getMetadata(currentClass)?.findColumnWithPropertyName(key)
846
+ };
847
+ };
848
+
849
+ // src/utility/cursor-pagination-utils.ts
850
+ function getValueFromOrderBy(orderBy, reversed = false) {
851
+ if (reversed) {
852
+ const value = getValueFromOrderBy(orderBy, false);
853
+ return value === "ASC" ? "DESC" : "ASC";
854
+ }
855
+ return typeof orderBy === "string" ? orderBy : orderBy.order;
856
+ }
857
+ function getNullsFromOrderBy(orderBy, reversed = false) {
858
+ if (reversed) {
859
+ const value = getNullsFromOrderBy(orderBy, false);
860
+ return value === "NULLS FIRST" ? "NULLS LAST" : "NULLS FIRST";
861
+ }
862
+ const nulls = typeof orderBy === "string" ? void 0 : orderBy.nulls;
863
+ if (!nulls) {
864
+ const value = getValueFromOrderBy(orderBy);
865
+ return value === "ASC" ? "NULLS FIRST" : "NULLS LAST";
866
+ }
867
+ return nulls;
868
+ }
869
+ function getOperator(orderBy) {
870
+ const value = getValueFromOrderBy(orderBy);
871
+ return value === "ASC" ? ">" : "<";
872
+ }
873
+ function getReversedTypeormOrderBy(orderBy) {
874
+ if (typeof orderBy === "string") {
875
+ return {
876
+ order: orderBy === "ASC" ? "DESC" : "ASC",
877
+ nulls: void 0
878
+ };
879
+ }
880
+ return {
881
+ order: orderBy.order === "ASC" ? "DESC" : "ASC",
882
+ nulls: orderBy.nulls ? orderBy.nulls === "NULLS FIRST" ? "NULLS LAST" : "NULLS FIRST" : void 0
883
+ };
884
+ }
885
+ function reverseQueryOrderBy(qb) {
886
+ const orderBys = getTypeormOrderBy(qb);
887
+ orderBys.forEach(({ key, direction }, i) => {
888
+ const reversed = getReversedTypeormOrderBy(direction);
889
+ if (i === 0) {
890
+ qb.orderBy(key, reversed.order, reversed.nulls);
891
+ } else {
892
+ qb.addOrderBy(key, reversed.order, reversed.nulls);
893
+ }
894
+ });
895
+ }
896
+ function getTypeormOrderBy(qb) {
897
+ const orderBy = qb.expressionMap.allOrderBys;
898
+ const orderByEntrys = Object.entries(orderBy);
899
+ return orderByEntrys.map(([key, value]) => ({
900
+ key,
901
+ direction: value
902
+ }));
903
+ }
904
+ function extractValueFromOrderByKey(obj, key, entityAliasName) {
905
+ const getField = (obj2, key2) => {
906
+ const value2 = obj2[key2];
907
+ if (value2 == null) return value2;
908
+ if (Array.isArray(value2)) {
909
+ return void 0;
910
+ }
911
+ return value2;
912
+ };
913
+ const [alias, field] = key.split(".");
914
+ if (alias === entityAliasName) {
915
+ return getField(obj, field);
916
+ }
917
+ const aliasParts = alias.split("_");
918
+ if (aliasParts.length === 1) {
919
+ const value2 = getField(obj, alias);
920
+ if (value2 == null) return value2;
921
+ return getField(value2, field);
922
+ }
923
+ const value = getField(obj, aliasParts[0]);
924
+ if (!value == null) return value;
925
+ return extractValueFromOrderByKey(
926
+ value,
927
+ `${aliasParts.slice(1).join("_")}.${field}`
928
+ );
929
+ }
930
+ function encodeBase64Url(str) {
931
+ return Buffer.from(str).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
932
+ }
933
+ function decodeBase64Url(str) {
934
+ str = str.replace(/-/g, "+").replace(/_/g, "/");
935
+ while (str.length % 4) str += "=";
936
+ return Buffer.from(str, "base64").toString();
937
+ }
938
+ async function getPaginatedResult(qb, entityClass, entityAliasName, take, cursor) {
939
+ const orderBys = getTypeormOrderBy(qb);
940
+ qb.take(take + 1);
941
+ let type = "next";
942
+ if (cursor) {
943
+ const data2 = SJSON.parse(decodeBase64Url(cursor));
944
+ type = data2.type;
945
+ const keys = Object.keys(data2.payload).filter(
946
+ (k) => qb.expressionMap.orderBys[k]
947
+ );
948
+ if (keys.length) {
949
+ const staircasedKeys = keys.map(
950
+ (key, i) => _2.range(i + 1).map((j) => keys[j])
951
+ );
952
+ const cursorKey = (key) => `_cursor_${key.replace(/\./g, "__").replace(/"/g, "")}`;
953
+ const expressionMatrix = staircasedKeys.map(
954
+ (keys2) => keys2.map((key, j) => {
955
+ const paramKey = cursorKey(key);
956
+ const cursorValue = data2.payload[key];
957
+ const orderBy = qb.expressionMap.orderBys[key];
958
+ const reversed = data2.type === "prev";
959
+ const order = getValueFromOrderBy(orderBy, reversed);
960
+ const nulls = getNullsFromOrderBy(orderBy, reversed);
961
+ const isLast = j === keys2.length - 1;
962
+ const subject = key.includes('"') ? getSubject(qb, key.replace(/"/g, "")) : key;
963
+ const mayBeNullAtEnd = () => {
964
+ const res = nulls === "NULLS LAST" && order === "ASC" || nulls === "NULLS FIRST" && order === "DESC";
965
+ if (reversed) {
966
+ return !res;
967
+ }
968
+ return res;
969
+ };
970
+ if (cursorValue == null) {
971
+ if (isLast) {
972
+ if (mayBeNullAtEnd()) {
973
+ return "__never__";
974
+ } else {
975
+ return `${subject} IS NOT NULL`;
976
+ }
977
+ } else {
978
+ return `${subject} IS NULL`;
979
+ }
980
+ } else {
981
+ if (isLast) {
982
+ const expr = `${subject} ${getOperator(order)} :${paramKey}`;
983
+ if (mayBeNullAtEnd() && queryColumnOptionsFromAlias(
984
+ qb,
985
+ entityClass,
986
+ entityAliasName,
987
+ key
988
+ )?.column?.isNullable) {
989
+ return `(${expr} OR ${subject} IS NULL)`;
990
+ }
991
+ return expr;
992
+ } else {
993
+ return `${subject} = :${paramKey}`;
994
+ }
995
+ }
996
+ })
997
+ ).filter((s) => !s.includes("__never__"));
998
+ if (expressionMatrix.length) {
999
+ qb.andWhere(
1000
+ new Brackets((sqb) => {
1001
+ const levelToBrackets = (level) => new Brackets((qb2) => {
1002
+ level.forEach((expr, i) => {
1003
+ if (i === 0) {
1004
+ qb2.where(expr);
1005
+ } else {
1006
+ qb2.andWhere(expr);
1007
+ }
1008
+ });
1009
+ });
1010
+ const [first, ...rest] = expressionMatrix;
1011
+ sqb.where(levelToBrackets(first));
1012
+ rest.forEach((level) => sqb.orWhere(levelToBrackets(level)));
1013
+ })
1014
+ ).setParameters(
1015
+ Object.fromEntries(
1016
+ Object.entries(data2.payload).filter(([k, v]) => qb.expressionMap.orderBys[k] && v != null).map(([k, v]) => [cursorKey(k), v])
1017
+ )
1018
+ );
1019
+ }
1020
+ }
1021
+ if (data2.type === "prev") {
1022
+ reverseQueryOrderBy(qb);
1023
+ }
1024
+ }
1025
+ const { raw, entities: data } = await qb.getRawAndEntities();
1026
+ const rawMapById = /* @__PURE__ */ new Map();
1027
+ const getRawFromEntity = (entity) => {
1028
+ const isNumberId = typeof entity.id === "number";
1029
+ const id = entity.id;
1030
+ if (rawMapById.has(id)) {
1031
+ return rawMapById.get(id);
1032
+ }
1033
+ return raw.find((r) => {
1034
+ let id2 = r[`${entityAliasName}_id`];
1035
+ if (isNumberId) {
1036
+ id2 = Number(id2);
1037
+ if (isNaN(id2)) return false;
1038
+ }
1039
+ if (id2 == null) return false;
1040
+ rawMapById[id2] = r;
1041
+ return id2 === entity.id;
1042
+ });
1043
+ };
1044
+ const enough = data.length > take;
1045
+ const hasNext = enough || type === "prev";
1046
+ const hasPrev = cursor && (enough || type === "next");
1047
+ if (enough) {
1048
+ data.pop();
1049
+ }
1050
+ if (type === "prev") {
1051
+ data.reverse();
1052
+ }
1053
+ const generateCursor = (type2, data2) => {
1054
+ const targetObject = type2 === "prev" ? data2[0] : data2[data2.length - 1];
1055
+ const payload = Object.fromEntries(
1056
+ orderBys.map(({ key }) => {
1057
+ const value = !key.includes(".") || key.includes('"') ? getRawFromEntity(targetObject)?.[key.replace(/"/g, "")] : extractValueFromOrderByKey(targetObject, key, entityAliasName);
1058
+ return [key, value];
1059
+ }).filter((s) => s[1] !== void 0)
1060
+ );
1061
+ return encodeBase64Url(SJSON.stringify({ type: type2, payload }));
1062
+ };
1063
+ return {
1064
+ data,
1065
+ paginatedResult: {
1066
+ nextCursor: hasNext ? generateCursor("next", data) : void 0,
1067
+ previousCursor: hasPrev ? generateCursor("prev", data) : void 0
1068
+ }
1069
+ };
1070
+ }
1071
+
1072
+ // src/crud-base.ts
1073
+ import PQueue from "p-queue";
1074
+ var Relation = (name, options = {}) => {
1075
+ return { name, inner: false, ...options };
1076
+ };
1077
+ var Inner = (name, options = {}) => {
1078
+ return Relation(name, { inner: true, ...options });
1079
+ };
1080
+ var loadedParsers = /* @__PURE__ */ new Set();
1081
+ var loadFullTextQueue = new PQueue({
1082
+ concurrency: 1
1083
+ });
1084
+ var CrudBase = class {
1085
+ constructor(entityClass, repo, crudOptions) {
1086
+ this.entityClass = entityClass;
1087
+ this.repo = repo;
1088
+ this.crudOptions = crudOptions;
1089
+ this.entityName = this.entityClass.name;
1090
+ this.entityReturnMessageDto = ReturnMessageDto(this.entityClass);
1091
+ this.importEntryDto = ImportEntryDto(this.entityClass);
1092
+ this.importReturnMessageDto = ReturnMessageDto([this.importEntryDto]);
1093
+ this.entityPaginatedReturnMessageDto = PaginatedReturnMessageDto(
1094
+ this.entityClass
1095
+ );
1096
+ this.entityCursorPaginatedReturnMessageDto = CursorPaginationReturnMessageDto(this.entityClass);
1097
+ this.entityRelations = filterRelations(
1098
+ this.entityClass,
1099
+ this.crudOptions.relations,
1100
+ (r) => !r.computed
1101
+ );
1102
+ this.extraGetQuery = this.crudOptions.extraGetQuery || ((qb) => {
1103
+ });
1104
+ this.log = new ConsoleLogger(`${this.entityClass.name}Service`);
1105
+ this._typeormRelations = getTypeormRelations(this.entityClass);
1106
+ }
1107
+ _cleanEntityNotInResultFields(ent) {
1108
+ const visited = /* @__PURE__ */ new Set();
1109
+ const runSingleObject = (o, cl) => {
1110
+ if (visited.has(o)) {
1111
+ return o;
1112
+ }
1113
+ const fields = getNotInResultFields(
1114
+ cl,
1115
+ this.crudOptions.keepEntityVersioningDates
1116
+ );
1117
+ for (const field of fields) {
1118
+ delete o[field];
1119
+ }
1120
+ visited.add(o);
1121
+ for (const relation of this._typeormRelations) {
1122
+ const propertyName = relation.propertyName;
1123
+ if (o[propertyName]) {
1124
+ if (Array.isArray(o[propertyName])) {
1125
+ o[propertyName] = o[propertyName].map(
1126
+ (r) => runSingleObject(r, relation.propertyClass)
1127
+ );
1128
+ } else {
1129
+ o[propertyName] = runSingleObject(
1130
+ o[propertyName],
1131
+ relation.propertyClass
1132
+ );
1133
+ }
1134
+ }
1135
+ }
1136
+ return o;
1137
+ };
1138
+ return runSingleObject(ent, this.entityClass);
1139
+ }
1140
+ cleanEntityNotInResultFields(ents) {
1141
+ if (Array.isArray(ents)) {
1142
+ return ents.map((ent) => this._cleanEntityNotInResultFields(ent));
1143
+ } else {
1144
+ return this._cleanEntityNotInResultFields(ents);
1145
+ }
1146
+ }
1147
+ async _batchCreate(ents, beforeCreate, skipErrors = false) {
1148
+ const entsWithId = ents.filter((ent) => ent.id != null);
1149
+ const result = await this.repo.manager.transaction(async (mdb) => {
1150
+ let skipped = [];
1151
+ const repo = mdb.getRepository(this.entityClass);
1152
+ let entsToSave = ents;
1153
+ if (entsWithId.length) {
1154
+ const entIds = entsWithId.map((ent) => ent.id);
1155
+ const entIdChunks = _3.chunk(entIds, 65535);
1156
+ const existingEnts = (await Promise.all(
1157
+ entIdChunks.map(
1158
+ (chunk) => repo.find({
1159
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
1160
+ // @ts-ignore
1161
+ where: {
1162
+ id: In(chunk)
1163
+ },
1164
+ select: this.crudOptions.createOrUpdate ? void 0 : ["id", "deleteTime"],
1165
+ withDeleted: true
1166
+ })
1167
+ )
1168
+ )).flat();
1169
+ if (existingEnts.length) {
1170
+ const existingEntsWithoutDeleteTime = existingEnts.filter(
1171
+ (ent) => ent.deleteTime == null
1172
+ );
1173
+ const existingEntsWithDeleteTime = existingEnts.filter(
1174
+ (ent) => ent.deleteTime != null
1175
+ );
1176
+ if (existingEntsWithoutDeleteTime.length) {
1177
+ if (this.crudOptions.createOrUpdate) {
1178
+ const existingIdMap = /* @__PURE__ */ new Map();
1179
+ existingEntsWithoutDeleteTime.forEach((ent) => {
1180
+ existingIdMap.set(ent.id, ent);
1181
+ });
1182
+ entsToSave = [];
1183
+ for (const ent of ents) {
1184
+ if (existingIdMap.has(ent.id)) {
1185
+ const existingEnt = existingIdMap.get(ent.id);
1186
+ Object.assign(existingEnt, ent);
1187
+ entsToSave.push(existingEnt);
1188
+ } else {
1189
+ entsToSave.push(ent);
1190
+ }
1191
+ }
1192
+ } else {
1193
+ if (!skipErrors) {
1194
+ throw new BlankReturnMessageDto2(
1195
+ 404,
1196
+ `${this.entityName} ID ${existingEntsWithoutDeleteTime.join(
1197
+ ","
1198
+ )} already exists`
1199
+ ).toException();
1200
+ }
1201
+ const existingEntsWithoutDeleteTimeIdSet = new Set(
1202
+ existingEntsWithoutDeleteTime.map((e) => e.id)
1203
+ );
1204
+ const skippedEnts = ents.filter(
1205
+ (ent) => existingEntsWithoutDeleteTimeIdSet.has(ent.id)
1206
+ );
1207
+ skipped = skippedEnts.map((ent) => ({
1208
+ result: "Already exists",
1209
+ entry: ent
1210
+ }));
1211
+ const skippedEntsSet = new Set(skippedEnts);
1212
+ entsToSave = ents.filter((ent) => !skippedEntsSet.has(ent));
1213
+ }
1214
+ }
1215
+ if (existingEntsWithDeleteTime.length) {
1216
+ await repo.delete(
1217
+ existingEntsWithDeleteTime.map((ent) => ent.id)
1218
+ );
1219
+ }
1220
+ }
1221
+ }
1222
+ if (beforeCreate) {
1223
+ await beforeCreate(repo);
1224
+ }
1225
+ try {
1226
+ const entChunksToSave = _3.chunk(
1227
+ entsToSave,
1228
+ Math.floor(
1229
+ 65535 / Math.max(1, Object.keys(entsToSave[0] || {}).length)
1230
+ )
1231
+ );
1232
+ let results = [];
1233
+ for (const entChunk of entChunksToSave) {
1234
+ const savedChunk = await repo.save(entChunk);
1235
+ results = results.concat(savedChunk);
1236
+ }
1237
+ return {
1238
+ results,
1239
+ skipped
1240
+ };
1241
+ } catch (e) {
1242
+ this.log.error(
1243
+ `Failed to create entity ${JSON.stringify(
1244
+ entsToSave
1245
+ )}: ${e.toString()}`
1246
+ );
1247
+ throw new BlankReturnMessageDto2(500, "Internal error").toException();
1248
+ }
1249
+ });
1250
+ return result;
1251
+ }
1252
+ async create(_ent, beforeCreate) {
1253
+ if (!_ent) {
1254
+ throw new BlankReturnMessageDto2(400, "Invalid entity").toException();
1255
+ }
1256
+ let ent = new this.entityClass();
1257
+ Object.assign(
1258
+ ent,
1259
+ omit(_ent, ...this._typeormRelations.map((r) => r.propertyName))
1260
+ );
1261
+ const invalidReason = ent.isValidInCreate();
1262
+ if (invalidReason) {
1263
+ throw new BlankReturnMessageDto2(400, invalidReason).toException();
1264
+ }
1265
+ const savedEnt = await this.repo.manager.transaction(async (mdb) => {
1266
+ const repo = mdb.getRepository(this.entityClass);
1267
+ if (ent.id != null) {
1268
+ const existingEnt = await repo.findOne({
1269
+ where: { id: ent.id },
1270
+ select: this.crudOptions.createOrUpdate ? void 0 : ["id", "deleteTime"],
1271
+ withDeleted: true
1272
+ });
1273
+ if (existingEnt) {
1274
+ if (existingEnt.deleteTime) {
1275
+ await repo.delete(existingEnt.id);
1276
+ } else if (this.crudOptions.createOrUpdate) {
1277
+ Object.assign(existingEnt, ent);
1278
+ ent = existingEnt;
1279
+ } else {
1280
+ throw new BlankReturnMessageDto2(
1281
+ 404,
1282
+ `${this.entityName} ID ${ent.id} already exists`
1283
+ ).toException();
1284
+ }
1285
+ }
1286
+ }
1287
+ if (beforeCreate) {
1288
+ await beforeCreate(repo);
1289
+ }
1290
+ await ent.beforeCreate?.();
1291
+ try {
1292
+ const savedEnt2 = await repo.save(ent);
1293
+ await savedEnt2.afterCreate?.();
1294
+ return this.cleanEntityNotInResultFields(savedEnt2);
1295
+ } catch (e) {
1296
+ this.log.error(
1297
+ `Failed to create entity ${JSON.stringify(ent)}: ${e.toString()}`
1298
+ );
1299
+ throw new BlankReturnMessageDto2(500, "Internal error").toException();
1300
+ }
1301
+ });
1302
+ return new this.entityReturnMessageDto(200, "success", savedEnt);
1303
+ }
1304
+ get entityAliasName() {
1305
+ return camelCase(this.entityName);
1306
+ }
1307
+ _applyQueryRelation(qb, relation) {
1308
+ const { name } = relation;
1309
+ const relationUnit = name.split(".");
1310
+ const base = relationUnit.length === 1 ? this.entityAliasName : relationUnit.slice(0, relationUnit.length - 1).join("_");
1311
+ const property = relationUnit[relationUnit.length - 1];
1312
+ const properyAlias = relationUnit.join("_");
1313
+ const methodName = relation.inner ? "innerJoin" : "leftJoin";
1314
+ if (!relation.noSelect) {
1315
+ qb.addSelect(properyAlias);
1316
+ }
1317
+ qb[methodName](
1318
+ `${base}.${property}`,
1319
+ properyAlias,
1320
+ relation.extraCondition || void 0,
1321
+ relation.extraConditionFields || void 0
1322
+ );
1323
+ }
1324
+ _applyQueryRelations(qb) {
1325
+ for (const relation of this.entityRelations) {
1326
+ if (typeof relation === "string") {
1327
+ this._applyQueryRelation(qb, { name: relation });
1328
+ } else {
1329
+ this._applyQueryRelation(qb, relation);
1330
+ }
1331
+ }
1332
+ }
1333
+ _applyQueryFilters(qb, ent) {
1334
+ const queryFields = reflector.getArray(
1335
+ "queryConditionFields",
1336
+ this.entityClass
1337
+ );
1338
+ for (const field of queryFields) {
1339
+ const condition = reflector.get(
1340
+ "queryCondition",
1341
+ this.entityClass,
1342
+ field
1343
+ );
1344
+ if (condition) {
1345
+ condition(ent, qb, this.entityAliasName, field);
1346
+ }
1347
+ }
1348
+ }
1349
+ queryBuilder() {
1350
+ return this.repo.createQueryBuilder(this.entityAliasName);
1351
+ }
1352
+ async findOne(id, extraQuery = () => {
1353
+ }) {
1354
+ const query = this.queryBuilder().where(`${this.entityAliasName}.id = :id`, { id }).take(1);
1355
+ this._applyQueryRelations(query);
1356
+ this.extraGetQuery(query);
1357
+ extraQuery(query);
1358
+ query.take(1);
1359
+ let ent;
1360
+ try {
1361
+ ent = await query.getOne();
1362
+ } catch (e) {
1363
+ const [sql, params] = query.getQueryAndParameters();
1364
+ this.log.error(
1365
+ `Failed to read entity ID ${id} with SQL ${sql} param ${params.join(
1366
+ ","
1367
+ )}: ${e.toString()}`
1368
+ );
1369
+ throw new BlankReturnMessageDto2(500, "Internal error").toException();
1370
+ }
1371
+ if (!ent) {
1372
+ throw new BlankReturnMessageDto2(
1373
+ 404,
1374
+ `${this.entityName} ID ${id} not found.`
1375
+ ).toException();
1376
+ }
1377
+ await ent.afterGet?.();
1378
+ return new this.entityReturnMessageDto(
1379
+ 200,
1380
+ "success",
1381
+ this.cleanEntityNotInResultFields(ent)
1382
+ );
1383
+ }
1384
+ async _preFindAll(ent, extraQuery = () => {
1385
+ }) {
1386
+ const query = this.queryBuilder().where("1 = 1");
1387
+ const newEnt = new this.entityClass();
1388
+ if (ent) {
1389
+ Object.assign(newEnt, ent);
1390
+ await newEnt?.beforeGet?.();
1391
+ newEnt.applyQuery(query, this.entityAliasName);
1392
+ }
1393
+ this._applyQueryRelations(query);
1394
+ this._applyQueryFilters(query, newEnt);
1395
+ const pageSettings = newEnt instanceof PageSettingsDto ? newEnt : Object.assign(new PageSettingsDto(), newEnt);
1396
+ this.extraGetQuery(query);
1397
+ extraQuery(query);
1398
+ return { query, newEnt, pageSettings };
1399
+ }
1400
+ async findAll(ent, extraQuery = () => {
1401
+ }) {
1402
+ const { query, pageSettings } = await this._preFindAll(ent, extraQuery);
1403
+ pageSettings.applyPaginationQuery(query);
1404
+ try {
1405
+ const [data, count] = await query.getManyAndCount();
1406
+ await Promise.all(data.map((ent2) => ent2.afterGet?.()));
1407
+ return new this.entityPaginatedReturnMessageDto(
1408
+ 200,
1409
+ "success",
1410
+ this.cleanEntityNotInResultFields(data),
1411
+ count,
1412
+ pageSettings.getActualPageSettings()
1413
+ );
1414
+ } catch (e) {
1415
+ const [sql, params] = query.getQueryAndParameters();
1416
+ this.log.error(
1417
+ `Failed to read entity cond ${JSON.stringify(
1418
+ ent
1419
+ )} with SQL ${sql} param ${params.join(",")}: ${e.toString()}`
1420
+ );
1421
+ throw new BlankReturnMessageDto2(500, "Internal error").toException();
1422
+ }
1423
+ }
1424
+ async findAllCursorPaginated(ent, extraQuery = () => {
1425
+ }) {
1426
+ const { query, pageSettings } = await this._preFindAll(ent, extraQuery);
1427
+ try {
1428
+ const { data, paginatedResult } = await getPaginatedResult(
1429
+ query,
1430
+ this.entityClass,
1431
+ this.entityAliasName,
1432
+ pageSettings.getRecordsPerPage(),
1433
+ ent.paginationCursor
1434
+ );
1435
+ await Promise.all(data.map((ent2) => ent2.afterGet?.()));
1436
+ return new this.entityCursorPaginatedReturnMessageDto(
1437
+ 200,
1438
+ "success",
1439
+ this.cleanEntityNotInResultFields(data),
1440
+ paginatedResult
1441
+ );
1442
+ } catch (e) {
1443
+ const [sql, params] = query.getQueryAndParameters();
1444
+ this.log.error(
1445
+ `Failed to read entity cond ${JSON.stringify(
1446
+ ent
1447
+ )} with SQL ${sql} param ${params.join(",")}: ${e.toString()}`
1448
+ );
1449
+ throw new BlankReturnMessageDto2(500, "Internal error").toException();
1450
+ }
1451
+ }
1452
+ async update(id, entPart, cond = {}) {
1453
+ let result;
1454
+ const ent = new this.entityClass();
1455
+ Object.assign(ent, entPart);
1456
+ const invalidReason = ent.isValidInUpdate();
1457
+ if (invalidReason) {
1458
+ throw new BlankReturnMessageDto2(400, invalidReason).toException();
1459
+ }
1460
+ await ent.beforeUpdate?.();
1461
+ try {
1462
+ result = await this.repo.update(
1463
+ {
1464
+ id,
1465
+ ...cond
1466
+ },
1467
+ ent
1468
+ );
1469
+ } catch (e) {
1470
+ this.log.error(
1471
+ `Failed to update entity ID ${id} to ${JSON.stringify(
1472
+ entPart
1473
+ )}: ${e.toString()}`
1474
+ );
1475
+ throw new BlankReturnMessageDto2(500, "Internal error").toException();
1476
+ }
1477
+ if (!result.affected) {
1478
+ throw new BlankReturnMessageDto2(
1479
+ 404,
1480
+ `${this.entityName} ID ${id} not found.`
1481
+ ).toException();
1482
+ }
1483
+ return new BlankReturnMessageDto2(200, "success");
1484
+ }
1485
+ async delete(id, cond = {}) {
1486
+ let result;
1487
+ const searchCond = {
1488
+ id,
1489
+ ...cond
1490
+ };
1491
+ try {
1492
+ result = await (this.crudOptions.hardDelete || !this.repo.manager.connection.getMetadata(this.entityClass).deleteDateColumn ? this.repo.delete(searchCond) : this.repo.softDelete(searchCond));
1493
+ } catch (e) {
1494
+ this.log.error(`Failed to delete entity ID ${id}: ${e.toString()}`);
1495
+ throw new BlankReturnMessageDto2(500, "Internal error").toException();
1496
+ }
1497
+ if (!result.affected) {
1498
+ throw new BlankReturnMessageDto2(
1499
+ 404,
1500
+ `${this.entityName} ID ${id} not found.`
1501
+ ).toException();
1502
+ }
1503
+ return new BlankReturnMessageDto2(200, "success");
1504
+ }
1505
+ async importEntities(_ents, extraChecking) {
1506
+ const ents = _ents.map((ent) => {
1507
+ const newEnt = new this.entityClass();
1508
+ Object.assign(
1509
+ newEnt,
1510
+ omit(ent, ...this._typeormRelations.map((r) => r.propertyName))
1511
+ );
1512
+ return newEnt;
1513
+ });
1514
+ const invalidResults = _3.compact(
1515
+ await Promise.all(
1516
+ ents.map(async (ent) => {
1517
+ const reason = ent.isValidInCreate();
1518
+ if (reason) {
1519
+ return { entry: ent, result: reason };
1520
+ }
1521
+ if (extraChecking) {
1522
+ const reason2 = await extraChecking(ent);
1523
+ if (reason2) {
1524
+ return { entry: ent, result: reason2 };
1525
+ }
1526
+ }
1527
+ })
1528
+ )
1529
+ );
1530
+ const remainingEnts = ents.filter(
1531
+ (ent) => !invalidResults.find((result) => result.entry === ent)
1532
+ );
1533
+ await Promise.all(remainingEnts.map((ent) => ent.beforeCreate?.()));
1534
+ const data = await this._batchCreate(remainingEnts, void 0, true);
1535
+ await Promise.all(data.results.map((e) => e.afterCreate?.()));
1536
+ const results = [
1537
+ ...invalidResults,
1538
+ ...data.skipped,
1539
+ ...data.results.map((e) => ({ entry: e, result: "OK" }))
1540
+ ];
1541
+ return new this.importReturnMessageDto(
1542
+ 200,
1543
+ "success",
1544
+ results.map((r) => {
1545
+ const entry = new this.importEntryDto();
1546
+ Object.assign(entry, r);
1547
+ entry.entry = this.cleanEntityNotInResultFields(r.entry);
1548
+ return entry;
1549
+ })
1550
+ );
1551
+ }
1552
+ async exists(id) {
1553
+ const ent = await this.repo.findOne({ where: { id }, select: ["id"] });
1554
+ return !!ent;
1555
+ }
1556
+ async _loadFullTextIndex() {
1557
+ const fields = reflector.getArray(
1558
+ "queryFullTextColumnFields",
1559
+ this.entityClass
1560
+ );
1561
+ const metadata = this.repo.metadata;
1562
+ const tableName = metadata.tableName;
1563
+ const sqls = [];
1564
+ for (const field of fields) {
1565
+ const options = reflector.get(
1566
+ "queryFullTextColumn",
1567
+ this.entityClass,
1568
+ field
1569
+ );
1570
+ if (!options) continue;
1571
+ const configurationName = options.configuration;
1572
+ const parser = options.parser;
1573
+ if (parser && !loadedParsers.has(parser)) {
1574
+ loadedParsers.add(parser);
1575
+ sqls.push(
1576
+ `CREATE EXTENSION IF NOT EXISTS ${parser};`,
1577
+ `DROP TEXT SEARCH CONFIGURATION IF EXISTS ${configurationName};`,
1578
+ `CREATE TEXT SEARCH CONFIGURATION ${configurationName} (PARSER = ${parser});`,
1579
+ `ALTER TEXT SEARCH CONFIGURATION ${configurationName} ADD MAPPING FOR n, v, a, i, e, l WITH simple;`
1580
+ );
1581
+ }
1582
+ const indexName = `idx_fulltext_${this.entityName}_${field}`;
1583
+ sqls.push(
1584
+ `CREATE INDEX IF NOT EXISTS "${indexName}" ON "${tableName}" USING GIN (to_tsvector('${configurationName}', "${field}"));`
1585
+ );
1586
+ }
1587
+ if (sqls.length) {
1588
+ await this.repo.manager.query(sqls.join("\n"));
1589
+ }
1590
+ }
1591
+ async onModuleInit() {
1592
+ await loadFullTextQueue.add(() => this._loadFullTextIndex());
1593
+ }
1594
+ };
1595
+ function CrudService(entityClass, crudOptions = {}) {
1596
+ return class CrudServiceImpl extends CrudBase {
1597
+ constructor(repo) {
1598
+ super(entityClass, repo, crudOptions);
1599
+ }
1600
+ };
1601
+ }
1602
+
1603
+ // src/restful.ts
1604
+ import {
1605
+ Body,
1606
+ Delete,
1607
+ Get,
1608
+ HttpCode,
1609
+ Param,
1610
+ ParseIntPipe,
1611
+ Patch,
1612
+ Post,
1613
+ Query
1614
+ } from "@nestjs/common";
1615
+ import {
1616
+ BlankReturnMessageDto as BlankReturnMessageDto3,
1617
+ MergeMethodDecorators,
1618
+ PaginatedReturnMessageDto as PaginatedReturnMessageDto2,
1619
+ ReturnMessageDto as ReturnMessageDto2
1620
+ } from "nesties";
1621
+ import {
1622
+ ApiBadRequestResponse,
1623
+ ApiBody,
1624
+ ApiInternalServerErrorResponse,
1625
+ ApiNotFoundResponse,
1626
+ ApiOkResponse,
1627
+ ApiOperation,
1628
+ ApiParam,
1629
+ ApiProperty as ApiProperty5,
1630
+ IntersectionType,
1631
+ OmitType as OmitType2,
1632
+ PartialType
1633
+ } from "@nestjs/swagger";
1634
+ import _4, { upperFirst } from "lodash";
1635
+
1636
+ // src/utility/rename-class.ts
1637
+ function RenameClass(cls, name) {
1638
+ Object.defineProperty(cls, "name", { value: name });
1639
+ return cls;
1640
+ }
1641
+
1642
+ // src/restful.ts
1643
+ import { DECORATORS } from "@nestjs/swagger/dist/constants";
1644
+
1645
+ // src/bases/base-restful-controller.ts
1646
+ var RestfulMethods = [
1647
+ "findOne",
1648
+ "findAll",
1649
+ "create",
1650
+ "update",
1651
+ "delete",
1652
+ "import"
1653
+ ];
1654
+ var BaseRestfulController = class {
1655
+ constructor(serviceOrRepo, _options = {}) {
1656
+ this._options = _options;
1657
+ if (serviceOrRepo instanceof CrudBase) {
1658
+ this.service = serviceOrRepo;
1659
+ } else {
1660
+ const crudServiceClass = CrudService(this._options.entityClass, {
1661
+ relations: this._options.relations
1662
+ });
1663
+ this.service = new crudServiceClass(serviceOrRepo);
1664
+ }
1665
+ }
1666
+ findOne(id) {
1667
+ return this.service.findOne(id);
1668
+ }
1669
+ findAll(dto) {
1670
+ if (this._options.paginateType === "cursor") {
1671
+ return this.service.findAllCursorPaginated(dto);
1672
+ }
1673
+ if (this._options.paginateType === "offset") {
1674
+ return this.service.findAll(dto);
1675
+ }
1676
+ dto["recordsPerPage"] ??= 99999;
1677
+ return this.service.findAll(dto);
1678
+ }
1679
+ create(dto) {
1680
+ return this.service.create(dto);
1681
+ }
1682
+ update(id, dto) {
1683
+ return this.service.update(id, dto);
1684
+ }
1685
+ delete(id) {
1686
+ return this.service.delete(id);
1687
+ }
1688
+ import(data) {
1689
+ return this.service.importEntities(data.data);
1690
+ }
1691
+ };
1692
+
1693
+ // src/utility/omit-type-exclude.ts
1694
+ import { OmitType } from "@nestjs/swagger";
1695
+ import { Exclude as Exclude2 } from "class-transformer";
1696
+ var OmitTypeExclude = (cl, keys) => {
1697
+ const omitted = OmitType(cl, keys);
1698
+ for (const key of keys) {
1699
+ Exclude2()(omitted.prototype, key);
1700
+ }
1701
+ return omitted;
1702
+ };
1703
+
1704
+ // src/restful.ts
1705
+ var getCurrentLevelRelations = (relations) => relations.filter((r) => !r.includes("."));
1706
+ var getNextLevelRelations = (relations, enteringField) => relations.filter((r) => r.includes(".") && r.startsWith(`${enteringField}.`)).map((r) => r.split(".").slice(1).join("."));
1707
+ var RestfulFactory = class _RestfulFactory {
1708
+ constructor(entityClass, options = {}, __resolveVisited = /* @__PURE__ */ new Map()) {
1709
+ this.entityClass = entityClass;
1710
+ this.options = options;
1711
+ this.__resolveVisited = __resolveVisited;
1712
+ this.fieldsToOmit = _4.uniq([
1713
+ ...getSpecificFields(this.entityClass, "notColumn"),
1714
+ ...this.options.fieldsToOmit || [],
1715
+ ...getTypeormRelations(this.entityClass).map(
1716
+ (r) => r.propertyName
1717
+ )
1718
+ ]);
1719
+ this.basicInputDto = OmitTypeExclude(
1720
+ this.entityClass,
1721
+ this.fieldsToOmit
1722
+ );
1723
+ this.createDto = RenameClass(
1724
+ OmitTypeExclude(
1725
+ this.basicInputDto,
1726
+ getSpecificFields(this.entityClass, "notWritable")
1727
+ ),
1728
+ `Create${this.entityClass.name}Dto`
1729
+ );
1730
+ this.importDto = ImportDataDto(this.createDto);
1731
+ this.findAllDto = RenameClass(
1732
+ PartialType(
1733
+ OmitTypeExclude(
1734
+ this.entityClass instanceof PageSettingsDto ? this.basicInputDto : IntersectionType(
1735
+ this.basicInputDto,
1736
+ PageSettingsDto
1737
+ ),
1738
+ getSpecificFields(this.entityClass, "notQueryable")
1739
+ )
1740
+ ),
1741
+ `Find${this.entityClass.name}Dto`
1742
+ );
1743
+ this.findAllCursorPaginatedDto = RenameClass(
1744
+ IntersectionType(
1745
+ OmitTypeExclude(this.findAllDto, ["pageCount"]),
1746
+ CursorPaginationDto
1747
+ ),
1748
+ `Find${this.entityClass.name}CursorPaginatedDto`
1749
+ );
1750
+ this.updateDto = RenameClass(
1751
+ PartialType(
1752
+ OmitTypeExclude(
1753
+ this.createDto,
1754
+ getSpecificFields(this.entityClass, "notChangeable")
1755
+ )
1756
+ ),
1757
+ `Update${this.entityClass.name}Dto`
1758
+ );
1759
+ this.entityResultDto = this.resolveEntityResultDto();
1760
+ this.entityCreateResultDto = RenameClass(
1761
+ OmitType2(this.entityResultDto, [
1762
+ ...getTypeormRelations(this.entityClass).map(
1763
+ (r) => r.propertyName
1764
+ ),
1765
+ ...getSpecificFields(
1766
+ this.entityClass,
1767
+ "notColumn",
1768
+ (m) => !m.keepInCreate
1769
+ )
1770
+ ]),
1771
+ `${this.getEntityClassName()}CreateResultDto`
1772
+ );
1773
+ this.entityReturnMessageDto = ReturnMessageDto2(this.entityResultDto);
1774
+ this.entityCreateReturnMessageDto = ReturnMessageDto2(
1775
+ this.entityCreateResultDto
1776
+ );
1777
+ this.entityArrayReturnMessageDto = PaginatedReturnMessageDto2(
1778
+ this.entityResultDto
1779
+ );
1780
+ this.entityCursorPaginationReturnMessageDto = CursorPaginationReturnMessageDto(this.entityResultDto);
1781
+ this.importReturnMessageDto = ReturnMessageDto2([
1782
+ ImportEntryDto(this.entityCreateResultDto)
1783
+ ]);
1784
+ // eslint-disable-next-line @typescript-eslint/ban-types
1785
+ this.idType = Reflect.getMetadata(
1786
+ "design:type",
1787
+ this.entityClass.prototype,
1788
+ "id"
1789
+ );
1790
+ if (options.relations) {
1791
+ filterRelations(entityClass, options.relations);
1792
+ }
1793
+ }
1794
+ getEntityClassName() {
1795
+ return this.options.entityClassName || this.entityClass.name;
1796
+ }
1797
+ resolveEntityResultDto() {
1798
+ const relations = getTypeormRelations(this.entityClass);
1799
+ const currentLevelRelations = this.options.relations && new Set(
1800
+ getCurrentLevelRelations(
1801
+ this.options.relations.map(extractRelationName)
1802
+ )
1803
+ );
1804
+ const outputFieldsToOmit = /* @__PURE__ */ new Set([
1805
+ ...getNotInResultFields(
1806
+ this.entityClass,
1807
+ this.options.keepEntityVersioningDates
1808
+ ),
1809
+ ...this.options.outputFieldsToOmit || [],
1810
+ ...this.options.relations ? relations.map((r) => r.propertyName).filter((r) => !currentLevelRelations.has(r)) : []
1811
+ ]);
1812
+ const resultDto = OmitType2(this.entityClass, [...outputFieldsToOmit]);
1813
+ for (const relation of relations) {
1814
+ if (outputFieldsToOmit.has(relation.propertyName)) continue;
1815
+ const replace = (useClass) => {
1816
+ const oldApiProperty = Reflect.getMetadata(
1817
+ DECORATORS.API_MODEL_PROPERTIES,
1818
+ this.entityClass.prototype,
1819
+ relation.propertyName
1820
+ ) || {};
1821
+ ApiProperty5({
1822
+ ...oldApiProperty,
1823
+ required: false,
1824
+ type: () => relation.isArray ? [useClass[0]] : useClass[0]
1825
+ })(resultDto.prototype, relation.propertyName);
1826
+ };
1827
+ const existing = this.__resolveVisited.get(relation.propertyClass);
1828
+ if (existing) {
1829
+ replace(existing);
1830
+ } else {
1831
+ if (!this.__resolveVisited.has(this.entityClass) && !this.options.relations) {
1832
+ this.__resolveVisited.set(this.entityClass, [null]);
1833
+ }
1834
+ const relationFactory = new _RestfulFactory(
1835
+ relation.propertyClass,
1836
+ {
1837
+ entityClassName: `${this.getEntityClassName()}${this.options.relations ? upperFirst(relation.propertyName) : relation.propertyClass.name}`,
1838
+ relations: this.options.relations && getNextLevelRelations(
1839
+ this.options.relations.map(extractRelationName),
1840
+ relation.propertyName
1841
+ ),
1842
+ keepEntityVersioningDates: this.options.keepEntityVersioningDates
1843
+ },
1844
+ this.__resolveVisited
1845
+ );
1846
+ const relationResultDto = relationFactory.entityResultDto;
1847
+ replace([relationResultDto]);
1848
+ if (!this.options.relations) {
1849
+ this.__resolveVisited.set(relation.propertyClass, [
1850
+ relationResultDto
1851
+ ]);
1852
+ }
1853
+ }
1854
+ }
1855
+ const res = RenameClass(
1856
+ resultDto,
1857
+ `${this.getEntityClassName()}ResultDto`
1858
+ );
1859
+ const currentContainer = this.__resolveVisited.get(this.entityClass);
1860
+ if (currentContainer) {
1861
+ currentContainer[0] = res;
1862
+ }
1863
+ return res;
1864
+ }
1865
+ usePrefix(methodDec, path) {
1866
+ if (path) {
1867
+ if (this.options.prefix) {
1868
+ return methodDec(`${this.options.prefix}/${path}`);
1869
+ } else {
1870
+ return methodDec(path);
1871
+ }
1872
+ } else {
1873
+ if (this.options.prefix) {
1874
+ return methodDec(this.options.prefix);
1875
+ } else {
1876
+ return methodDec();
1877
+ }
1878
+ }
1879
+ }
1880
+ create(extras = {}) {
1881
+ return MergeMethodDecorators([
1882
+ this.usePrefix(Post),
1883
+ HttpCode(200),
1884
+ ApiOperation({
1885
+ summary: `Create a new ${this.getEntityClassName()}`,
1886
+ ...extras
1887
+ }),
1888
+ ApiBody({ type: this.createDto }),
1889
+ ApiOkResponse({ type: this.entityCreateReturnMessageDto }),
1890
+ ApiBadRequestResponse({
1891
+ type: BlankReturnMessageDto3,
1892
+ description: `The ${this.getEntityClassName()} is not valid`
1893
+ })
1894
+ ]);
1895
+ }
1896
+ createParam() {
1897
+ return Body(CreatePipe());
1898
+ }
1899
+ findOne(extras = {}) {
1900
+ return MergeMethodDecorators([
1901
+ this.usePrefix(Get, ":id"),
1902
+ ApiOperation({
1903
+ summary: `Find a ${this.getEntityClassName()} by id`,
1904
+ ...extras
1905
+ }),
1906
+ ApiParam({ name: "id", type: this.idType, required: true }),
1907
+ ApiOkResponse({ type: this.entityReturnMessageDto }),
1908
+ ApiNotFoundResponse({
1909
+ type: BlankReturnMessageDto3,
1910
+ description: `The ${this.getEntityClassName()} with the given id was not found`
1911
+ })
1912
+ ]);
1913
+ }
1914
+ idParam() {
1915
+ if (this.idType === Number) {
1916
+ return Param("id", ParseIntPipe);
1917
+ } else {
1918
+ return Param("id");
1919
+ }
1920
+ }
1921
+ findAll(extras = {}) {
1922
+ return MergeMethodDecorators([
1923
+ this.usePrefix(Get),
1924
+ ApiOperation({
1925
+ summary: `Find all ${this.getEntityClassName()}`,
1926
+ ...extras
1927
+ }),
1928
+ ApiOkResponse({ type: this.entityArrayReturnMessageDto })
1929
+ ]);
1930
+ }
1931
+ findAllCursorPaginated(extras = {}) {
1932
+ return MergeMethodDecorators([
1933
+ this.usePrefix(Get),
1934
+ ApiOperation({
1935
+ summary: `Find all ${this.getEntityClassName()}`,
1936
+ ...extras
1937
+ }),
1938
+ ApiOkResponse({ type: this.entityCursorPaginationReturnMessageDto })
1939
+ ]);
1940
+ }
1941
+ findAllParam() {
1942
+ return Query(GetPipe());
1943
+ }
1944
+ update(extras = {}) {
1945
+ return MergeMethodDecorators([
1946
+ this.usePrefix(Patch, ":id"),
1947
+ HttpCode(200),
1948
+ ApiOperation({
1949
+ summary: `Update a ${this.getEntityClassName()} by id`,
1950
+ ...extras
1951
+ }),
1952
+ ApiParam({ name: "id", type: this.idType, required: true }),
1953
+ ApiBody({ type: this.updateDto }),
1954
+ ApiOkResponse({ type: BlankReturnMessageDto3 }),
1955
+ ApiNotFoundResponse({
1956
+ type: BlankReturnMessageDto3,
1957
+ description: `The ${this.getEntityClassName()} with the given id was not found`
1958
+ }),
1959
+ ApiBadRequestResponse({
1960
+ type: BlankReturnMessageDto3,
1961
+ description: `The ${this.getEntityClassName()} is not valid`
1962
+ }),
1963
+ ApiInternalServerErrorResponse({
1964
+ type: BlankReturnMessageDto3,
1965
+ description: "Internal error"
1966
+ })
1967
+ ]);
1968
+ }
1969
+ updateParam() {
1970
+ return Body(UpdatePipe());
1971
+ }
1972
+ delete(extras = {}) {
1973
+ return MergeMethodDecorators([
1974
+ this.usePrefix(Delete, ":id"),
1975
+ HttpCode(200),
1976
+ ApiOperation({
1977
+ summary: `Delete a ${this.getEntityClassName()} by id`,
1978
+ ...extras
1979
+ }),
1980
+ ApiParam({ name: "id", type: this.idType, required: true }),
1981
+ ApiOkResponse({ type: BlankReturnMessageDto3 }),
1982
+ ApiNotFoundResponse({
1983
+ type: BlankReturnMessageDto3,
1984
+ description: `The ${this.getEntityClassName()} with the given id was not found`
1985
+ }),
1986
+ ApiInternalServerErrorResponse({
1987
+ type: BlankReturnMessageDto3,
1988
+ description: "Internal error"
1989
+ })
1990
+ ]);
1991
+ }
1992
+ import(extras = {}) {
1993
+ return MergeMethodDecorators([
1994
+ Post("import"),
1995
+ HttpCode(200),
1996
+ ApiOperation({
1997
+ summary: `Import ${this.getEntityClassName()}`,
1998
+ ...extras
1999
+ }),
2000
+ ApiBody({ type: this.importDto }),
2001
+ ApiOkResponse({ type: this.importReturnMessageDto }),
2002
+ ApiInternalServerErrorResponse({
2003
+ type: BlankReturnMessageDto3,
2004
+ description: "Internal error"
2005
+ })
2006
+ ]);
2007
+ }
2008
+ baseController(routeOptions = {}) {
2009
+ const _this = this;
2010
+ const cl = class SpecificRestfulController extends BaseRestfulController {
2011
+ constructor(service) {
2012
+ super(service, {
2013
+ paginateType: routeOptions.paginateType || "offset",
2014
+ relations: _this.options.relations,
2015
+ entityClass: _this.entityClass
2016
+ });
2017
+ }
2018
+ };
2019
+ const anyTrueWritten = RestfulMethods.some(
2020
+ (m) => routeOptions?.routes?.[m]?.enabled === true
2021
+ );
2022
+ const validMethods = RestfulMethods.filter((m) => {
2023
+ const value = routeOptions?.routes?.[m]?.enabled;
2024
+ if (value === false) return false;
2025
+ if (value === true) return true;
2026
+ return !anyTrueWritten || value === true;
2027
+ });
2028
+ const useDecorators = {
2029
+ findOne: {
2030
+ paramTypes: [this.idType],
2031
+ paramDecorators: () => [this.idParam()],
2032
+ methodDecorators: () => [this.findOne()]
2033
+ },
2034
+ findAll: {
2035
+ paramTypes: [
2036
+ routeOptions.paginateType === "cursor" ? this.findAllCursorPaginatedDto : routeOptions.paginateType === "none" ? OmitType2(this.findAllDto, [
2037
+ "pageCount",
2038
+ "recordsPerPage"
2039
+ ]) : this.findAllDto
2040
+ ],
2041
+ paramDecorators: () => [this.findAllParam()],
2042
+ methodDecorators: () => [
2043
+ routeOptions.paginateType === "cursor" ? this.findAllCursorPaginated() : this.findAll()
2044
+ ]
2045
+ },
2046
+ create: {
2047
+ paramTypes: [this.createDto],
2048
+ paramDecorators: () => [this.createParam()],
2049
+ methodDecorators: () => [this.create()]
2050
+ },
2051
+ update: {
2052
+ paramTypes: [this.idType, this.updateDto],
2053
+ paramDecorators: () => [this.idParam(), this.updateParam()],
2054
+ methodDecorators: () => [this.update()]
2055
+ },
2056
+ delete: {
2057
+ paramTypes: [this.idType],
2058
+ paramDecorators: () => [this.idParam()],
2059
+ methodDecorators: () => [this.delete()]
2060
+ },
2061
+ import: {
2062
+ paramTypes: [this.importDto],
2063
+ paramDecorators: () => [this.createParam()],
2064
+ methodDecorators: () => [this.import()]
2065
+ }
2066
+ };
2067
+ for (const method of validMethods) {
2068
+ const methodImpl = function namedMethodImpl(...args) {
2069
+ return BaseRestfulController.prototype[method].apply(this, args);
2070
+ };
2071
+ Object.defineProperty(methodImpl, "name", {
2072
+ value: method,
2073
+ configurable: true
2074
+ });
2075
+ cl.prototype[method] = methodImpl;
2076
+ const paramDecorators = useDecorators[method].paramDecorators();
2077
+ const paramTypes = useDecorators[method].paramTypes;
2078
+ const methodDecorators = [
2079
+ ...useDecorators[method].methodDecorators(),
2080
+ ...routeOptions?.routes?.[method]?.methodDecorators || [],
2081
+ ...routeOptions?.globalMethodDecorators || []
2082
+ ];
2083
+ paramDecorators.forEach((paramDecorator, index) => {
2084
+ paramDecorator(cl.prototype, method, index);
2085
+ });
2086
+ Reflect.defineMetadata(
2087
+ "design:paramtypes",
2088
+ paramTypes,
2089
+ cl.prototype,
2090
+ method
2091
+ );
2092
+ const baseDescriptor = Object.getOwnPropertyDescriptor(
2093
+ BaseRestfulController.prototype,
2094
+ method
2095
+ );
2096
+ if (baseDescriptor) {
2097
+ Reflect.defineMetadata(
2098
+ "design:type",
2099
+ baseDescriptor.value,
2100
+ cl.prototype,
2101
+ method
2102
+ );
2103
+ Reflect.defineMetadata(
2104
+ "design:returntype",
2105
+ Promise,
2106
+ cl.prototype,
2107
+ method
2108
+ );
2109
+ }
2110
+ methodDecorators.forEach((methodDecorator) => {
2111
+ const descriptor = Object.getOwnPropertyDescriptor(
2112
+ cl.prototype,
2113
+ method
2114
+ );
2115
+ methodDecorator(cl.prototype, method, descriptor);
2116
+ Object.defineProperty(cl.prototype, method, descriptor);
2117
+ });
2118
+ }
2119
+ return RenameClass(cl, `${this.getEntityClassName()}Controller`);
2120
+ }
2121
+ crudService(options = {}) {
2122
+ return CrudService(this.entityClass, {
2123
+ relations: this.options.relations,
2124
+ ...options
2125
+ });
2126
+ }
2127
+ };
2128
+ export {
2129
+ BlankCursorPaginationReturnMessageDto,
2130
+ BoolColumn,
2131
+ CreatePipe,
2132
+ CrudBase,
2133
+ CrudService,
2134
+ CursorPaginationDto,
2135
+ CursorPaginationReturnMessageDto,
2136
+ DateColumn,
2137
+ EnumColumn,
2138
+ FloatColumn,
2139
+ GenericCursorPaginationReturnMessageDto,
2140
+ GetPipe,
2141
+ IdBase,
2142
+ ImportDataBaseDto,
2143
+ ImportDataDto,
2144
+ ImportEntryBaseDto,
2145
+ ImportEntryDto,
2146
+ Inner,
2147
+ IntColumn,
2148
+ JsonColumn,
2149
+ NotChangeable,
2150
+ NotColumn,
2151
+ NotInResult,
2152
+ NotQueryable,
2153
+ NotWritable,
2154
+ PageSettingsDto,
2155
+ QueryColumn,
2156
+ QueryCondition,
2157
+ QueryEqual,
2158
+ QueryEqualZeroNullable,
2159
+ QueryFullText,
2160
+ QueryGreater,
2161
+ QueryGreaterEqual,
2162
+ QueryLess,
2163
+ QueryLessEqual,
2164
+ QueryLike,
2165
+ QueryMatchBoolean,
2166
+ QueryNotEqual,
2167
+ QueryOperator,
2168
+ QuerySearch,
2169
+ Relation,
2170
+ RelationComputed,
2171
+ RestfulFactory,
2172
+ StringColumn,
2173
+ StringIdBase,
2174
+ TimeBase,
2175
+ UpdatePipe,
2176
+ applyQueryMatchBoolean,
2177
+ applyQueryProperty,
2178
+ applyQueryPropertyLike,
2179
+ applyQueryPropertySearch,
2180
+ applyQueryPropertyZeroNullable,
2181
+ createQueryCondition,
2182
+ createQueryOperator
2183
+ };
2184
+ //# sourceMappingURL=index.mjs.map