vsrepo 1.4.2 → 2.1.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 (151) hide show
  1. package/README.md +827 -1322
  2. package/README.pt-BR.md +833 -1325
  3. package/dist/VSRepoAdapter.d.ts +109 -0
  4. package/dist/VSRepoAdapter.js +18 -0
  5. package/dist/VSRepository.d.ts +166 -1201
  6. package/dist/VSRepository.js +327 -237
  7. package/dist/decorators/dynamic-method.decorator.d.ts +25 -0
  8. package/dist/decorators/dynamic-method.decorator.js +38 -0
  9. package/dist/decorators/query-method.decorator.d.ts +27 -0
  10. package/dist/decorators/query-method.decorator.js +45 -0
  11. package/dist/errors/VSRepoAdapterError.d.ts +22 -0
  12. package/dist/errors/VSRepoAdapterError.js +31 -0
  13. package/dist/errors/VSRepoError.d.ts +15 -0
  14. package/dist/errors/VSRepoError.js +21 -0
  15. package/dist/index.d.ts +38 -1
  16. package/dist/index.js +28 -15
  17. package/dist/internal/constants/debug-arg-symbol.constant.d.ts +1 -0
  18. package/dist/internal/constants/debug-arg-symbol.constant.js +4 -0
  19. package/dist/internal/constants/dynamic-methods-key.constant.d.ts +1 -0
  20. package/dist/internal/constants/query-methods-key.constant.d.ts +1 -0
  21. package/dist/internal/constants/query-methods-key.constant.js +4 -0
  22. package/dist/internal/enums/adapter-error-code.enum.d.ts +125 -0
  23. package/dist/internal/enums/adapter-error-code.enum.js +129 -0
  24. package/dist/internal/enums/transaction-isolation-level.enum.d.ts +16 -0
  25. package/dist/internal/enums/transaction-isolation-level.enum.js +20 -0
  26. package/dist/internal/enums/vs-log-level.enum.d.ts +18 -0
  27. package/dist/internal/enums/vs-log-level.enum.js +22 -0
  28. package/dist/internal/enums/vsrepo-error-type.enum.d.ts +19 -0
  29. package/dist/internal/enums/vsrepo-error-type.enum.js +23 -0
  30. package/dist/internal/resolvers/dynamic-methods.resolver.d.ts +23 -0
  31. package/dist/internal/resolvers/dynamic-methods.resolver.js +910 -0
  32. package/dist/internal/resolvers/merge-wheres.resolver.d.ts +7 -0
  33. package/dist/internal/resolvers/merge-wheres.resolver.js +26 -0
  34. package/dist/internal/utils/uncapitalize.util.d.ts +1 -0
  35. package/dist/internal/utils/vs-logger.util.d.ts +25 -0
  36. package/dist/internal/utils/vs-logger.util.js +139 -0
  37. package/dist/internal/validators/decorators.validator.d.ts +8 -0
  38. package/dist/internal/validators/decorators.validator.js +75 -0
  39. package/dist/internal/validators/schemas/ordering.schema.d.ts +3 -0
  40. package/dist/internal/validators/schemas/ordering.schema.js +38 -0
  41. package/dist/internal/validators/schemas/pagination.schema.d.ts +6 -0
  42. package/dist/internal/validators/schemas/pagination.schema.js +40 -0
  43. package/dist/internal/validators/schemas/where.schema.d.ts +7 -0
  44. package/dist/internal/validators/schemas/where.schema.js +42 -0
  45. package/dist/internal/validators/vsrepo.validator.d.ts +40 -0
  46. package/dist/internal/validators/vsrepo.validator.js +182 -0
  47. package/dist/types/adapter/adapter-method-options.type.d.ts +24 -0
  48. package/dist/types/adapter/adapter-query-options.type.d.ts +5 -0
  49. package/dist/types/decorators/dynamic-method-options.type.d.ts +14 -0
  50. package/dist/types/decorators/query-method-options.type.d.ts +15 -0
  51. package/dist/types/dynamic-methods/dynamic-method-customization.type.d.ts +7 -0
  52. package/dist/types/dynamic-methods/dynamic-method-info.type.d.ts +18 -0
  53. package/dist/types/dynamic-methods/dynamic-method-where-ops.type.d.ts +6 -0
  54. package/dist/types/utils/count-result.type.d.ts +9 -0
  55. package/dist/types/utils/decimal-like.type.d.ts +23 -0
  56. package/dist/types/utils/deep-partial.type.d.ts +14 -0
  57. package/dist/types/utils/keys-of-type.type.d.ts +20 -0
  58. package/dist/types/utils/methods-options.type.d.ts +23 -0
  59. package/dist/types/utils/numeric-keys.type.d.ts +24 -0
  60. package/dist/types/utils/numeric-like.type.d.ts +10 -0
  61. package/dist/types/utils/ordering.type.d.ts +39 -0
  62. package/dist/types/utils/pagination.type.d.ts +11 -0
  63. package/dist/types/utils/perform-data.type.d.ts +4 -0
  64. package/dist/types/utils/primitive.type.d.ts +7 -0
  65. package/dist/types/utils/query-method-arg.type.d.ts +27 -0
  66. package/dist/types/utils/restrict-method-options.type.d.ts +14 -0
  67. package/dist/types/utils/see-mode.type.d.ts +12 -0
  68. package/dist/types/vsrepo/vsrepo-args.type.d.ts +9 -0
  69. package/dist/types/vsrepo/vsrepo-method.type.d.ts +4 -0
  70. package/dist/types/vsrepo/vsrepo-method.type.js +2 -0
  71. package/dist/types/vsrepo/vsrepo-options.type.d.ts +34 -0
  72. package/dist/types/vsrepo/vsrepo-options.type.js +2 -0
  73. package/dist/types/vsrepo/vsrepo-orm-types.type.d.ts +17 -0
  74. package/dist/types/vsrepo/vsrepo-orm-types.type.js +2 -0
  75. package/dist/types/vsrepo/vsrepo-pretty-where.type.d.ts +7 -0
  76. package/dist/types/vsrepo/vsrepo-pretty-where.type.js +2 -0
  77. package/dist/types/vsrepo/vsrepo-query-options.type.d.ts +17 -0
  78. package/dist/types/vsrepo/vsrepo-query-options.type.js +2 -0
  79. package/dist/types/vsrepo/vsrepo-query.type.d.ts +5 -0
  80. package/dist/types/vsrepo/vsrepo-query.type.js +2 -0
  81. package/dist/types/vsrepo/vsrepo-relations.type.d.ts +26 -0
  82. package/dist/types/vsrepo/vsrepo-relations.type.js +2 -0
  83. package/dist/types/vsrepo/vsrepo-resolve-args-data.type.d.ts +19 -0
  84. package/dist/types/vsrepo/vsrepo-resolve-args-data.type.js +2 -0
  85. package/dist/types/vsrepo/vsrepo-select.type.d.ts +15 -0
  86. package/dist/types/vsrepo/vsrepo-select.type.js +2 -0
  87. package/dist/types/vsrepo/vsrepo-transaction-options.type.d.ts +12 -0
  88. package/dist/types/vsrepo/vsrepo-transaction-options.type.js +2 -0
  89. package/dist/types/vsrepo/vsrepo-ugly-where.type.d.ts +9 -0
  90. package/dist/types/vsrepo/vsrepo-ugly-where.type.js +2 -0
  91. package/dist/types/vsrepo/vsrepo-where.type.d.ts +99 -0
  92. package/dist/types/vsrepo/vsrepo-where.type.js +2 -0
  93. package/package.json +16 -37
  94. package/README-DynamicRepo.md +0 -625
  95. package/README-DynamicRepo.pt-BR.md +0 -625
  96. package/dist/DynamicRepository.d.ts +0 -497
  97. package/dist/DynamicRepository.js +0 -26
  98. package/dist/VSRepoError.d.ts +0 -83
  99. package/dist/VSRepoError.js +0 -17
  100. package/dist/internal/decorators/dynamic-method.decorator.js +0 -14
  101. package/dist/internal/decorators/query-method.decorator.js +0 -20
  102. package/dist/internal/entities/dynamic-method-metadata.entity.js +0 -26
  103. package/dist/internal/errors/vs-repo.error.js +0 -31
  104. package/dist/internal/resolvers/base-methods.resolve.js +0 -541
  105. package/dist/internal/resolvers/create-update-payloads-with-relations.resolve.js +0 -143
  106. package/dist/internal/resolvers/data-payload-with-relations.resolve.js +0 -60
  107. package/dist/internal/resolvers/dbAndPrismaArgs.resolve.js +0 -63
  108. package/dist/internal/resolvers/dynamic-method-customization.resolve.js +0 -57
  109. package/dist/internal/resolvers/dynamic-method-info.resolve.js +0 -279
  110. package/dist/internal/resolvers/dynamic-methods-metadata.resolve.js +0 -15
  111. package/dist/internal/resolvers/merge-wheres.resolve.js +0 -22
  112. package/dist/internal/resolvers/pretty-wheres.resolve.js +0 -87
  113. package/dist/internal/resolvers/select.resolve.js +0 -7
  114. package/dist/internal/resolvers/specific-where.resolve.js +0 -84
  115. package/dist/internal/resolvers/ugly-where.resolve.js +0 -178
  116. package/dist/internal/utils/logger.util.js +0 -21
  117. package/dist/internal/utils/schemas.util.js +0 -31
  118. package/dist/internal/validation/build-config.validate.js +0 -84
  119. package/dist/internal/validation/constructor-config.validate.js +0 -64
  120. package/dist/internal/validation/dynamic-method-config.validate.js +0 -19
  121. package/dist/internal/validation/extension.validate.js +0 -15
  122. package/dist/internal/validation/is-object.validate.js +0 -6
  123. package/dist/internal/validation/method-options.validate.js +0 -42
  124. package/dist/internal/validation/obj-with-relations.validate.js +0 -37
  125. package/dist/internal/validation/prisma-client.validate.js +0 -10
  126. package/dist/internal/validation/query-method-arg.validate.js +0 -22
  127. package/dist/internal/validation/query-method-options.validate.js +0 -24
  128. package/scripts/configure-prisma-import.mjs +0 -283
  129. package/scripts/copy-types.mjs +0 -24
  130. /package/dist/{internal/decorators/types/dynamic-method-config.type.js → types/adapter/adapter-method-options.type.js} +0 -0
  131. /package/dist/{internal/errors/types/vs-repo-error-type.type.js → types/adapter/adapter-query-options.type.js} +0 -0
  132. /package/dist/{internal/errors/types/vs-repo-runtime-error-code.type.js → types/decorators/dynamic-method-options.type.js} +0 -0
  133. /package/dist/{internal/validation/types → types/decorators}/query-method-options.type.js +0 -0
  134. /package/dist/{internal/resolvers/types → types/dynamic-methods}/dynamic-method-customization.type.js +0 -0
  135. /package/dist/{internal/resolvers/types → types/dynamic-methods}/dynamic-method-info.type.js +0 -0
  136. /package/dist/{internal/resolvers/types → types/dynamic-methods}/dynamic-method-where-ops.type.js +0 -0
  137. /package/dist/{internal/resolvers/types/base-method-function.type.js → types/utils/count-result.type.js} +0 -0
  138. /package/dist/{internal/resolvers/types/pretty-where.type.js → types/utils/decimal-like.type.js} +0 -0
  139. /package/dist/{internal/resolvers/types/prisma-args.type.js → types/utils/deep-partial.type.js} +0 -0
  140. /package/dist/{internal/resolvers/types/repository-build-instance.type.js → types/utils/keys-of-type.type.js} +0 -0
  141. /package/dist/{internal/resolvers/types/resolve-db-and-prisma-args-data.type.js → types/utils/methods-options.type.js} +0 -0
  142. /package/dist/{internal/resolvers/types/ugly-where.type.js → types/utils/numeric-keys.type.js} +0 -0
  143. /package/dist/{internal/validation/types/base-methods.type.js → types/utils/numeric-like.type.js} +0 -0
  144. /package/dist/{internal/validation/types/build-config.type.js → types/utils/ordering.type.js} +0 -0
  145. /package/dist/{internal/validation/types → types/utils}/pagination.type.js +0 -0
  146. /package/dist/{internal/validation/types/constructor-config.type.js → types/utils/perform-data.type.js} +0 -0
  147. /package/dist/{internal/validation/types/method-options.type.js → types/utils/primitive.type.js} +0 -0
  148. /package/dist/{internal/validation/types → types/utils}/query-method-arg.type.js +0 -0
  149. /package/dist/{internal/validation/types/method.type.js → types/utils/restrict-method-options.type.js} +0 -0
  150. /package/dist/{internal/validation/types → types/utils}/see-mode.type.js +0 -0
  151. /package/dist/{internal/validation/types/relation.type.js → types/vsrepo/vsrepo-args.type.js} +0 -0
@@ -1,245 +1,335 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
2
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.setupVSRepo = exports.VSRepository = void 0;
18
- __exportStar(require("./internal/errors/vs-repo.error"), exports);
19
- const build_config_validate_1 = require("./internal/validation/build-config.validate");
20
- const constructor_config_validate_1 = require("./internal/validation/constructor-config.validate");
21
- const extension_validate_1 = require("./internal/validation/extension.validate");
22
- const prisma_client_validate_1 = require("./internal/validation/prisma-client.validate");
23
- const vs_repo_error_1 = require("./internal/errors/vs-repo.error");
24
- const base_methods_resolve_1 = require("./internal/resolvers/base-methods.resolve");
25
- const logger_util_1 = require("./internal/utils/logger.util");
26
- const dynamic_method_info_resolve_1 = require("./internal/resolvers/dynamic-method-info.resolve");
27
- const dynamic_method_customization_resolve_1 = require("./internal/resolvers/dynamic-method-customization.resolve");
28
- const pretty_wheres_resolve_1 = require("./internal/resolvers/pretty-wheres.resolve");
29
- const specific_where_resolve_1 = require("./internal/resolvers/specific-where.resolve");
30
- const dbAndPrismaArgs_resolve_1 = require("./internal/resolvers/dbAndPrismaArgs.resolve");
31
- const method_options_validate_1 = require("./internal/validation/method-options.validate");
32
- const query_method_arg_validate_1 = require("./internal/validation/query-method-arg.validate");
3
+ exports.VSRepository = void 0;
4
+ require("reflect-metadata");
5
+ const merge_wheres_resolver_1 = require("./internal/resolvers/merge-wheres.resolver");
6
+ const vs_logger_util_1 = require("./internal/utils/vs-logger.util");
7
+ const vs_log_level_enum_1 = require("./internal/enums/vs-log-level.enum");
8
+ const vsrepo_validator_1 = require("./internal/validators/vsrepo.validator");
9
+ const dynamic_methods_resolver_1 = require("./internal/resolvers/dynamic-methods.resolver");
10
+ const VSRepoError_1 = require("./errors/VSRepoError");
11
+ const vsrepo_error_type_enum_1 = require("./internal/enums/vsrepo-error-type.enum");
12
+ /**
13
+ * ORM-agnostic base repository, exposing a complete set of ready-to-use CRUD
14
+ * and soft-delete methods around an entity, plus any `@DynamicMethod`/`@QueryMethod`
15
+ * decorated methods declared on the subclass.
16
+ *
17
+ * Unlike the previous (v1) `VSRepository`, this class delegates every
18
+ * operation to a `VSRepoAdapter` instead of talking to Prisma directly, which
19
+ * is what allows it to work with any ORM/database that has an adapter
20
+ * implementation (e.g. Prisma, TypeORM).
21
+ *
22
+ * @template Entity Type of the entity managed by the repository.
23
+ * @template PKType Type of the entity's primary key value.
24
+ * @template OrmTypes ORM-specific client/transaction types. See `VSRepoOrmTypes`.
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * class UserRepository extends VSRepository<User, string> {
29
+ * constructor() {
30
+ * super({ pkName: "id", adapter: new VSRepoPrisma7Adapter(prisma, "user") });
31
+ * }
32
+ *
33
+ * @DynamicMethod()
34
+ * declare findByEmail: (email: string) => Promise<User[]>;
35
+ * }
36
+ *
37
+ * const userRepository = new UserRepository();
38
+ * const user = await userRepository.get("123");
39
+ * ```
40
+ *
41
+ * @publicApi
42
+ */
33
43
  class VSRepository {
34
- vsrepocache;
35
- tableName;
36
44
  pkName;
37
- softRemovekName;
38
- selectModels;
39
- includeModels;
40
- defaultSelectModel;
41
- requiredWhere;
42
- relations;
43
- methods;
45
+ adapter;
46
+ mergeWheresResolver;
47
+ logger;
48
+ validator;
49
+ softRemoveKey;
44
50
  defaultOrdering;
45
- constructor(config) {
46
- const validatedConfig = (0, constructor_config_validate_1.validateConstructorConfig)(config);
47
- this.vsrepocache = new Map();
48
- this.tableName = validatedConfig.tableName;
49
- this.pkName = validatedConfig.pkName;
50
- this.softRemovekName = validatedConfig.softRemovekName;
51
- this.selectModels = validatedConfig.selectModels;
52
- this.includeModels = validatedConfig.includeModels;
53
- this.defaultSelectModel = validatedConfig.defaultSelectModel;
54
- this.relations = validatedConfig.relations;
55
- this.requiredWhere = validatedConfig.requiredWhere;
56
- this.methods = validatedConfig.methods;
57
- this.defaultOrdering = validatedConfig.defaultOrdering;
58
- }
59
- extend(extensionFunc) {
60
- const extension = (0, extension_validate_1.validateExtension)(extensionFunc, this);
61
- const extended = Object.assign(Object.create(this), extension);
62
- if (Object.isFrozen(this)) {
63
- Object.freeze(extended);
64
- }
65
- return extended;
66
- }
67
- build(prisma, config, useInstance) {
68
- (0, prisma_client_validate_1.validatePrismaClient)(prisma, this);
69
- const buildInstance = useInstance ?? Object.create(this);
70
- buildInstance.prisma = prisma;
71
- if (buildInstance.softRemovekName &&
72
- prisma[buildInstance.tableName]["fields"][buildInstance.softRemovekName]["typeName"] !==
73
- "DateTime") {
74
- throw new vs_repo_error_1.VSRepoBuildError(`[VSRepository] (${buildInstance.tableName}: build) 'typeName' of 'softRemovekName' must be "DateTime": ${buildInstance.softRemovekName}`);
75
- }
76
- const validatedConfig = (0, build_config_validate_1.validateBuildConfig)(config ?? {}, buildInstance);
77
- (0, base_methods_resolve_1.resolveBaseMethods)(buildInstance, validatedConfig);
78
- const showWorking = validatedConfig.showWorking;
79
- const methods = buildInstance.methods;
80
- if (methods) {
81
- const methodsToMap = Object.keys(methods).filter(m => methods[m]?.map);
82
- if (showWorking)
83
- (0, logger_util_1.logger)("Keys to map:", "build", buildInstance.tableName, methodsToMap);
84
- for (let methodToMap of methodsToMap) {
85
- const originalKey = methodToMap;
86
- if (methods[originalKey]?.query) {
87
- const modifyingQueryMethod = methods[originalKey].query.modifying;
88
- const valueQueryMethod = methods[originalKey].query.value;
89
- buildInstance[originalKey] = async (arg) => {
90
- const queryArgValidated = (0, query_method_arg_validate_1.validateQueryMethodArg)(arg, buildInstance);
91
- const db = queryArgValidated.db ?? buildInstance.prisma;
92
- const start = showWorking
93
- ? (0, logger_util_1.performanceLoggerStart)(buildInstance.tableName, `Query method: ${originalKey} (Modifying: ${modifyingQueryMethod})`, queryArgValidated.args)
94
- : undefined;
95
- try {
96
- const result = await db[modifyingQueryMethod ? "$executeRawUnsafe" : "$queryRawUnsafe"](valueQueryMethod, ...queryArgValidated.args);
97
- if (showWorking)
98
- (0, logger_util_1.performanceLoggerEnd)(buildInstance.tableName, `Query method: ${originalKey} (Modifying: ${modifyingQueryMethod})`, start);
99
- return result;
100
- }
101
- catch (err) {
102
- throw err;
103
- }
104
- };
105
- continue;
106
- }
107
- methodToMap = methods[originalKey]?.proxyTo ?? methodToMap;
108
- const dynamicMethodInfo = (0, dynamic_method_info_resolve_1.resolveDynamicMethodInfo)(buildInstance, methodToMap, originalKey);
109
- const dynamicMethodCustomization = (0, dynamic_method_customization_resolve_1.resolveDynamicMethodCustomization)(buildInstance, dynamicMethodInfo, originalKey);
110
- const dynamicMethodWhereOps = {
111
- uglyWheres: [],
112
- prettyWheres: [],
113
- whereType: methods[originalKey]?.whereType ?? "extending",
114
- pushWhere: methods[originalKey]?.pushWhere,
115
- };
116
- if (!dynamicMethodInfo.ignoreWhere) {
117
- (0, pretty_wheres_resolve_1.resolvePrettyWheres)(dynamicMethodInfo, dynamicMethodWhereOps);
118
- // if (showWorking) {
119
- // const argsSimulation: any[] = [];
120
- // for (let x = 0; x < dynamicMethodInfo.argsCount; x++) {
121
- // argsSimulation[x] = "00";
122
- // }
123
- // logger(
124
- // `Where object resolved to ${methodToMap}:`,
125
- // "build",
126
- // buildInstance.tableName,
127
- // resolveSpecificWhere(
128
- // argsSimulation,
129
- // dynamicMethodWhereOps.prettyWheres,
130
- // ),
131
- // );
132
- // // logger(
133
- // // `Where object resolved to ${methodToMap}:`,
134
- // // "build",
135
- // // buildInstance.tableName,
136
- // // dynamicMethodWhereOps.prettyWheres,
137
- // // );
138
- // }
139
- }
140
- let select = undefined;
141
- if (dynamicMethodInfo.existsMode) {
142
- select = { [buildInstance.pkName]: true };
143
- }
144
- buildInstance.vsrepocache.set(originalKey, (args, methodOptions) => {
145
- if (dynamicMethodInfo.prismaArgsIndex !== undefined)
146
- return args.at(dynamicMethodInfo.prismaArgsIndex);
147
- const resolveDbAndPrismaArgsData = {
148
- instance: buildInstance,
149
- options: methodOptions,
150
- alreadyValidatedOptions: true,
151
- baseConfig: {
152
- active: true,
153
- defaultSelect: methods[originalKey]?.selectModel,
154
- ignoreRequiredWhere: dynamicMethodWhereOps.whereType === "overwrite",
155
- },
156
- withoutWhere: dynamicMethodInfo.ignoreWhere && !dynamicMethodInfo.onlyBaseWheres,
157
- specificSelect: select,
158
- pushWhere: dynamicMethodWhereOps.pushWhere,
159
- withoutSelect: dynamicMethodInfo.ignoreSelect,
160
- skipDuplicates: dynamicMethodCustomization.skipDuplicates,
161
- ordering: dynamicMethodCustomization.orderPosition !== undefined
162
- ? args.at(dynamicMethodCustomization.orderPosition)
163
- : dynamicMethodCustomization.injectOrdering,
164
- pagination: dynamicMethodCustomization.paginationPosition !== undefined
165
- ? args.at(dynamicMethodCustomization.paginationPosition)
166
- : dynamicMethodCustomization.injectPagination,
167
- dataPayload: dynamicMethodInfo.dataIndex !== undefined
168
- ? args.at(dynamicMethodInfo.dataIndex)
169
- : undefined,
170
- createPayload: dynamicMethodInfo.createIndex !== undefined
171
- ? args.at(dynamicMethodInfo.createIndex)
172
- : undefined,
173
- updatePayload: dynamicMethodInfo.updateIndex !== undefined
174
- ? args.at(dynamicMethodInfo.updateIndex)
175
- : undefined,
176
- withOrderingAndPagination: !dynamicMethodInfo.ignoreOrderByAndPagination,
177
- distinctKeys: dynamicMethodCustomization.distinctKeys,
178
- };
179
- if (!dynamicMethodInfo.ignoreWhere) {
180
- resolveDbAndPrismaArgsData.specificWhere = (0, specific_where_resolve_1.resolveSpecificWhere)(args, dynamicMethodWhereOps.prettyWheres);
181
- }
182
- else if (dynamicMethodInfo.onlyBaseWheres) {
183
- resolveDbAndPrismaArgsData.specificWhere =
184
- dynamicMethodInfo.whereIndex !== undefined
185
- ? args.at(dynamicMethodInfo.whereIndex)
186
- : {};
187
- }
188
- const { prismaArgs } = (0, dbAndPrismaArgs_resolve_1.resolveDbAndPrismaArgs)(resolveDbAndPrismaArgsData);
189
- return prismaArgs;
190
- });
191
- if (showWorking) {
192
- const argsSimulation = new Array(dynamicMethodInfo.argsCount).fill("00");
193
- const prismaArgs = buildInstance.vsrepocache.get(originalKey)(argsSimulation);
194
- (0, logger_util_1.logger)(`PrismaArgs preview for ${methodToMap}:`, "build", buildInstance.tableName, prismaArgs);
195
- }
196
- buildInstance[originalKey] = async (...args) => {
197
- let db = buildInstance.prisma;
198
- let methodOptions = undefined;
199
- if (args.length < dynamicMethodInfo.argsCount) {
200
- const missingParams = dynamicMethodInfo.whereParams
201
- .concat(dynamicMethodInfo.otherParams)
202
- .slice(args.length);
203
- throw new vs_repo_error_1.VSRepoRuntimeError(`[VSRepository] (${buildInstance.tableName}: runtime) Missing parameters: ${missingParams.join(", ")}`, "48670");
204
- }
205
- else if (args.length > dynamicMethodInfo.argsCount) {
206
- const optionsArg = args[args.length - 1];
207
- methodOptions = (0, method_options_validate_1.validateMethodOptions)(optionsArg, buildInstance);
208
- db = methodOptions.db ?? db;
209
- }
210
- else {
211
- args.push("1");
212
- }
213
- const prismaArgs = buildInstance.vsrepocache.get(originalKey)(args, methodOptions);
214
- const start = showWorking
215
- ? (0, logger_util_1.performanceLoggerStart)(buildInstance.tableName, dynamicMethodInfo.method, prismaArgs)
216
- : undefined;
217
- try {
218
- const result = await db[buildInstance.tableName][dynamicMethodInfo.method](prismaArgs);
219
- if (showWorking)
220
- (0, logger_util_1.performanceLoggerEnd)(buildInstance.tableName, dynamicMethodInfo.method, start);
221
- if (dynamicMethodInfo.existsMode) {
222
- return !!result;
223
- }
224
- return result;
225
- }
226
- catch (err) {
227
- // logger(
228
- // `Fatal error when executing ${dynamicMethodInfo.method}:`,
229
- // "runtime",
230
- // buildInstance.tableName,
231
- // { prismaArgs },
232
- // );
233
- throw err;
234
- }
235
- };
236
- }
237
- }
238
- if (validatedConfig.freeze)
239
- Object.freeze(buildInstance);
240
- return buildInstance;
51
+ /**
52
+ * This is a property managed by VSRepository, please don't modify it!!
53
+ * @internal
54
+ */
55
+ $vsrepocache = new Map();
56
+ /**
57
+ * Creates a configured instance of `VSRepository`, resolving and validating
58
+ * every `@DynamicMethod`/`@QueryMethod` declared on the subclass.
59
+ */
60
+ constructor(options) {
61
+ this.validator = new vsrepo_validator_1.VSRepoValidator();
62
+ const optionsValidated = this.validator.validateConstructorOptions(options);
63
+ this.adapter = optionsValidated.adapter;
64
+ this.pkName = optionsValidated.pkName;
65
+ this.softRemoveKey = optionsValidated.softRemoveKey;
66
+ this.defaultOrdering = optionsValidated.defaultOrdering;
67
+ this.mergeWheresResolver = new merge_wheres_resolver_1.MergeWheresResolver(this.softRemoveKey);
68
+ this.logger = new vs_logger_util_1.VSLogger(optionsValidated.logLevel ?? vs_log_level_enum_1.VSLogLevel.WARN, this.constructor.name + "Logger", optionsValidated.logSlowThresholdMs);
69
+ this.validator.setLogger(this.logger);
70
+ this.logger.logInfo(`Initializing ${this.constructor.name} (pk: '${String(this.pkName)}'` +
71
+ (this.softRemoveKey ? `, softRemoveKey: '${String(this.softRemoveKey)}'` : "") +
72
+ (this.defaultOrdering
73
+ ? `, defaultOrdering: ${JSON.stringify(this.defaultOrdering)}`
74
+ : "") +
75
+ `, adapter: ${this.adapter.constructor.name}` +
76
+ `)`);
77
+ const dynamicMethodsResolver = new dynamic_methods_resolver_1.DynamicMethodsResolver(this.logger, this.adapter, this.mergeWheresResolver, this.validator, this.defaultOrdering);
78
+ let dynamicMethodsCount = 0;
79
+ let queryMethodsCount = 0;
80
+ try {
81
+ const start = this.logger.startPerformLog("resolve dynamic methods");
82
+ dynamicMethodsCount = dynamicMethodsResolver.resolve(this);
83
+ this.logger.endPerformLog(start);
84
+ const startQuery = this.logger.startPerformLog("resolve query methods");
85
+ queryMethodsCount = dynamicMethodsResolver.resolveQueries(this);
86
+ this.logger.endPerformLog(startQuery);
87
+ }
88
+ catch (err) {
89
+ this.logger.logError(`Failed to initialize ${this.constructor.name}`, err);
90
+ throw err;
91
+ }
92
+ this.logger.logInfo(`${this.constructor.name} ready (${dynamicMethodsCount} dynamic method(s), ${queryMethodsCount} query method(s) resolved)`);
93
+ }
94
+ // * Loga o erro antes de lançar, pra guard clauses (mau uso da API) não passarem em silêncio
95
+ fail(message, type) {
96
+ this.logger.logError(`${this.constructor.name}: ${message}`);
97
+ throw new VSRepoError_1.VSRepoError(message, type);
98
+ }
99
+ wherePk(pk) {
100
+ return { [this.pkName]: pk };
101
+ }
102
+ wherePkIn(pks) {
103
+ return { [this.pkName]: { in: pks } };
104
+ }
105
+ async execBaseMethod(fn, methodName, optionsUnchecked, opsType = "common") {
106
+ const optionsChecked = opsType === "common"
107
+ ? this.validator.validateMethodOptions(optionsUnchecked)
108
+ : opsType === "restrict"
109
+ ? this.validator.validateRestrictMethodOptions(optionsUnchecked)
110
+ : this.validator.validateGetAllMethodOptions(optionsUnchecked);
111
+ optionsChecked.db ??= this.getDbClient();
112
+ const start = this.logger.startPerformLog("run " + methodName);
113
+ try {
114
+ const result = await fn(optionsChecked);
115
+ this.logger.endPerformLog(start);
116
+ return result;
117
+ }
118
+ catch (err) {
119
+ this.logger.endPerformLog(start);
120
+ // this.logger.logError(`Failed to run '${methodName}' on ${this.constructor.name}`, err);
121
+ throw err;
122
+ }
123
+ }
124
+ /**
125
+ * Runs `fn` inside a native transaction of the underlying ORM, sharing the
126
+ * transaction client (`tx`) across every repository call made within it.
127
+ */
128
+ async transaction(fn, options) {
129
+ if (typeof fn !== "function") {
130
+ this.fail("'fn' must be a valid function", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
131
+ }
132
+ return this.adapter.runInTransaction(fn, this.validator.validateTransactionOptions(options));
133
+ }
134
+ /** Returns the underlying ORM client instance used outside of transactions. */
135
+ getDbClient() {
136
+ return this.adapter.getDbClient();
137
+ }
138
+ /**
139
+ * Executes a raw query/statement directly against the underlying database.
140
+ *
141
+ * Use `$1`, `$2`, ... placeholders for values passed via `options.args` —
142
+ * never interpolate values directly into `query`, to avoid SQL injection.
143
+ * Set `options.modifying: true` for `INSERT`/`UPDATE`/`DELETE` statements.
144
+ *
145
+ * @example
146
+ * ```typescript
147
+ * const users = await userRepository.query<User[]>(
148
+ * 'SELECT * FROM "user" WHERE email = $1',
149
+ * { args: ["joao@email.com"] },
150
+ * );
151
+ *
152
+ * const affected = await userRepository.query<number>(
153
+ * 'UPDATE "user" SET active = true WHERE id = $1',
154
+ * { args: ["123"], modifying: true },
155
+ * );
156
+ * ```
157
+ */
158
+ async query(query, options) {
159
+ if (typeof query !== "string") {
160
+ this.fail("'query' must be a valid string", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
161
+ }
162
+ const optionsValidated = this.validator.validateQueryOptions(options);
163
+ optionsValidated.db ??= this.getDbClient();
164
+ const start = this.logger.startPerformLog("run query");
165
+ try {
166
+ const result = await this.adapter.query(query, {
167
+ args: optionsValidated.args,
168
+ db: optionsValidated.db,
169
+ modifying: optionsValidated.modifying ?? false,
170
+ });
171
+ this.logger.endPerformLog(start);
172
+ return result;
173
+ }
174
+ catch (err) {
175
+ this.logger.endPerformLog(start);
176
+ // this.logger.logError(`Failed to run 'query' on ${this.constructor.name}`, err);
177
+ throw err;
178
+ }
179
+ }
180
+ /** Fetches a record by its primary key (PK). */
181
+ async get(pk, options) {
182
+ return this.execBaseMethod(opt => this.adapter.findOne(this.mergeWheresResolver.resolve(opt.see, this.wherePk(pk)), opt), "get", options);
183
+ }
184
+ /** Fetches a record by PK and throws an Error if not found. */
185
+ async getOrThrow(pk, options) {
186
+ return this.execBaseMethod(opt => this.adapter.findOneOrThrow(this.mergeWheresResolver.resolve(opt.see, this.wherePk(pk)), opt), "getOrThrow", options);
187
+ }
188
+ /** Fetches multiple records by a list of primary keys (PKs). */
189
+ async getList(pks, options) {
190
+ if (!Array.isArray(pks)) {
191
+ this.fail("'pks' must be a valid array", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
192
+ }
193
+ return this.execBaseMethod(opt => this.adapter.findMany(this.mergeWheresResolver.resolve(opt.see, this.wherePkIn(pks)), opt), "getList", options);
194
+ }
195
+ /** Fetches all records (respects the repository's `defaultOrdering` unless `order` is provided). */
196
+ async getAll(options) {
197
+ return this.execBaseMethod((opt) => this.adapter.findMany(this.mergeWheresResolver.resolve(opt.see, {}), {
198
+ ...opt,
199
+ order: opt.order ?? this.defaultOrdering,
200
+ }), "getAll", options, "getAll");
201
+ }
202
+ /** Creates or updates (upsert) a record. */
203
+ async save(obj, options) {
204
+ return this.execBaseMethod(opt => this.adapter.save(obj, opt), "save", options);
205
+ }
206
+ /** Creates or updates (upsert) multiple records in a single operation. */
207
+ async saveList(objs, options) {
208
+ if (!Array.isArray(objs)) {
209
+ this.fail("'objs' must be a valid array", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
210
+ }
211
+ return this.execBaseMethod(opt => this.adapter.saveMany(objs, opt), "saveList", options);
212
+ }
213
+ /** Deletes a record identified by its primary key (PK). */
214
+ async remove(pk, options) {
215
+ return this.execBaseMethod(opt => this.adapter.delete(this.mergeWheresResolver.resolve(opt.see, this.wherePk(pk)), opt), "remove", options);
216
+ }
217
+ /** Deletes multiple records by their primary keys, returning the count of affected rows. */
218
+ async removeList(pks, options) {
219
+ if (!Array.isArray(pks)) {
220
+ this.fail("'pks' must be a valid array", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
221
+ }
222
+ return this.execBaseMethod(opt => this.adapter.deleteMany(this.mergeWheresResolver.resolve(opt.see, this.wherePkIn(pks)), opt), "removeList", options, "restrict");
223
+ }
224
+ /** Partially updates an existing record by its primary key (PK). */
225
+ async patch(pk, obj, options) {
226
+ return this.execBaseMethod(opt => this.adapter.update(this.mergeWheresResolver.resolve(opt.see, this.wherePk(pk)), obj, opt), "patch", options);
227
+ }
228
+ /**
229
+ * Fetches a record by PK and returns it deep-merged, in memory, with the
230
+ * provided object — does **not** persist anything (pass the result to
231
+ * `save`/`patch` yourself to write it).
232
+ */
233
+ async merge(pk, obj, options) {
234
+ return this.execBaseMethod(opt => this.adapter.merge(this.mergeWheresResolver.resolve(opt.see, this.wherePk(pk)), obj, opt), "merge", options);
235
+ }
236
+ /** Returns the total number of records. */
237
+ async total(options) {
238
+ return this.execBaseMethod(opt => this.adapter.count(this.mergeWheresResolver.resolve(opt.see, {}), opt), "total", options, "restrict");
239
+ }
240
+ /** Checks whether a record exists by its primary key (PK). */
241
+ async has(pk, options) {
242
+ return this.execBaseMethod(opt => this.adapter.exists(this.mergeWheresResolver.resolve(opt.see, this.wherePk(pk)), opt), "has", options, "restrict");
243
+ }
244
+ /** Marks a record as deleted (soft-delete). Requires `softRemoveKey` to be configured on the repository. */
245
+ async softRemove(pk, options) {
246
+ if (!this.softRemoveKey) {
247
+ this.fail("this method can only be used if you have configured 'softRemoveKey' in this repository.", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
248
+ }
249
+ const key = this.softRemoveKey;
250
+ return this.execBaseMethod(opt => this.adapter.update(this.mergeWheresResolver.resolve(opt.see ?? "all", this.wherePk(pk)), { [key]: new Date() }, opt), "softRemove", options);
251
+ }
252
+ /** Marks multiple records as deleted (soft-delete) in batch. Requires `softRemoveKey` to be configured on the repository. */
253
+ async softRemoveList(pks, options) {
254
+ if (!this.softRemoveKey) {
255
+ this.fail("this method can only be used if you have configured 'softRemoveKey' in this repository.", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
256
+ }
257
+ if (!Array.isArray(pks)) {
258
+ this.fail("'pks' must be a valid array", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
259
+ }
260
+ const key = this.softRemoveKey;
261
+ return this.execBaseMethod(opt => this.adapter.updateMany(this.mergeWheresResolver.resolve(opt.see ?? "all", this.wherePkIn(pks)), { [key]: new Date() }, opt), "softRemoveList", options, "restrict");
262
+ }
263
+ /** Restores a record previously marked as deleted (soft-delete). Requires `softRemoveKey` to be configured on the repository. */
264
+ async restore(pk, options) {
265
+ if (!this.softRemoveKey) {
266
+ this.fail("this method can only be used if you have configured 'softRemoveKey' in this repository.", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
267
+ }
268
+ const key = this.softRemoveKey;
269
+ return this.execBaseMethod(opt => this.adapter.update(this.mergeWheresResolver.resolve(opt.see ?? "all", this.wherePk(pk)), { [key]: null }, opt), "restore", options);
270
+ }
271
+ /** Restores multiple records previously marked as deleted (soft-delete) in batch. Requires `softRemoveKey` to be configured on the repository. */
272
+ async restoreList(pks, options) {
273
+ if (!this.softRemoveKey) {
274
+ this.fail("this method can only be used if you have configured 'softRemoveKey' in this repository.", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
275
+ }
276
+ if (!Array.isArray(pks)) {
277
+ this.fail("'pks' must be a valid array", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
278
+ }
279
+ const key = this.softRemoveKey;
280
+ return this.execBaseMethod(opt => this.adapter.updateMany(this.mergeWheresResolver.resolve(opt.see ?? "all", this.wherePkIn(pks)), { [key]: null }, opt), "restoreList", options, "restrict");
281
+ }
282
+ /**
283
+ * Atomically adds `value` to a numeric field of the record identified
284
+ * by `pk`, evaluated server-side against the row's current value (e.g.
285
+ * `saldo = saldo + value`) — not a fetch-then-save round trip.
286
+ */
287
+ async increment(pk, field, value, options) {
288
+ this.validator.assertIsNumericLike(value);
289
+ return this.execBaseMethod(opt => this.adapter.incrementOne(field, value, this.mergeWheresResolver.resolve(opt.see, this.wherePk(pk)), opt), "increment", options);
290
+ }
291
+ /** Same as {@link VSRepository.increment}, subtracting `value` instead of adding it. */
292
+ async decrement(pk, field, value, options) {
293
+ this.validator.assertIsNumericLike(value);
294
+ return this.execBaseMethod(opt => this.adapter.decrementOne(field, value, this.mergeWheresResolver.resolve(opt.see, this.wherePk(pk)), opt), "decrement", options);
295
+ }
296
+ /** Same as {@link VSRepository.increment}, multiplying the field's current value by `value`. */
297
+ async multiply(pk, field, value, options) {
298
+ this.validator.assertIsNumericLike(value);
299
+ return this.execBaseMethod(opt => this.adapter.multiplyOne(field, value, this.mergeWheresResolver.resolve(opt.see, this.wherePk(pk)), opt), "multiply", options);
300
+ }
301
+ /**
302
+ * Same as {@link VSRepository.increment}, dividing the field's current
303
+ * value by `value`. Division-by-zero behavior depends on the adapter/
304
+ * underlying database (see {@link VSRepoAdapter.divideOne}).
305
+ */
306
+ async divide(pk, field, value, options) {
307
+ this.validator.assertIsNumericLike(value);
308
+ return this.execBaseMethod(opt => this.adapter.divideOne(field, value, this.mergeWheresResolver.resolve(opt.see, this.wherePk(pk)), opt), "divide", options);
309
+ }
310
+ /**
311
+ * Returns the sum of a numeric field across every record matching
312
+ * `where` (all records if omitted), or `null` if none match — mirrors
313
+ * SQL's `SUM()`, which returns `NULL` (not `0`) over an empty set.
314
+ */
315
+ async sum(field, where, options) {
316
+ const validatedWhere = this.validator.validateWhere(where ?? {});
317
+ return this.execBaseMethod(opt => this.adapter.sum(field, this.mergeWheresResolver.resolve(opt.see, validatedWhere), opt), "sum", options, "restrict");
318
+ }
319
+ /** Same as {@link VSRepository.sum}, but the arithmetic mean instead of the total. */
320
+ async average(field, where, options) {
321
+ const validatedWhere = this.validator.validateWhere(where ?? {});
322
+ return this.execBaseMethod(opt => this.adapter.average(field, this.mergeWheresResolver.resolve(opt.see, validatedWhere), opt), "average", options, "restrict");
323
+ }
324
+ /** Same as {@link VSRepository.sum}, but the minimum value instead of the total. */
325
+ async min(field, where, options) {
326
+ const validatedWhere = this.validator.validateWhere(where ?? {});
327
+ return this.execBaseMethod(opt => this.adapter.min(field, this.mergeWheresResolver.resolve(opt.see, validatedWhere), opt), "min", options, "restrict");
328
+ }
329
+ /** Same as {@link VSRepository.sum}, but the maximum value instead of the total. */
330
+ async max(field, where, options) {
331
+ const validatedWhere = this.validator.validateWhere(where ?? {});
332
+ return this.execBaseMethod(opt => this.adapter.max(field, this.mergeWheresResolver.resolve(opt.see, validatedWhere), opt), "max", options, "restrict");
241
333
  }
242
334
  }
243
335
  exports.VSRepository = VSRepository;
244
- const setupVSRepo = () => (config) => new VSRepository(config);
245
- exports.setupVSRepo = setupVSRepo;
@@ -0,0 +1,25 @@
1
+ import { DynamicMethodOptions } from "../types/decorators/dynamic-method-options.type";
2
+ /**
3
+ * Property decorator used to declare a dynamic method on a `VSRepository` subclass.
4
+ *
5
+ * Applied to a `declare` class field whose name follows one of the supported
6
+ * dynamic-method patterns (e.g. `findByEmail`, `findManyByStatusPaginated`,
7
+ * `upsertById`), the method's behavior is inferred from the field name at
8
+ * construction time, optionally adjusted via `options`.
9
+ *
10
+ * @template T Entity type the decorated method operates on.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * class UserRepository extends VSRepository<User, string> {
15
+ * @DynamicMethod()
16
+ * declare findByEmail: (email: string) => Promise<User[]>;
17
+ *
18
+ * @DynamicMethod<User>({ injectOrdering: { createdAt: "desc" } })
19
+ * declare findByAge: (age: number) => Promise<User[]>;
20
+ * }
21
+ * ```
22
+ *
23
+ * @publicApi
24
+ */
25
+ export declare function DynamicMethod<T = any>(options?: DynamicMethodOptions<T>): PropertyDecorator;