vsrepo 1.4.1 → 2.0.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 (143) hide show
  1. package/README.md +723 -1338
  2. package/README.pt-BR.md +729 -1341
  3. package/dist/VSRepoAdapter.d.ts +71 -0
  4. package/dist/VSRepoAdapter.js +18 -0
  5. package/dist/VSRepository.d.ts +135 -1201
  6. package/dist/VSRepository.js +273 -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 +34 -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 +33 -0
  46. package/dist/internal/validators/vsrepo.validator.js +160 -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/deep-partial.type.d.ts +14 -0
  56. package/dist/types/utils/keys-of-type.type.d.ts +20 -0
  57. package/dist/types/utils/methods-options.type.d.ts +23 -0
  58. package/dist/types/utils/ordering.type.d.ts +39 -0
  59. package/dist/types/utils/pagination.type.d.ts +11 -0
  60. package/dist/types/utils/perform-data.type.d.ts +4 -0
  61. package/dist/types/utils/primitive.type.d.ts +6 -0
  62. package/dist/types/utils/query-method-arg.type.d.ts +27 -0
  63. package/dist/types/utils/see-mode.type.d.ts +12 -0
  64. package/dist/types/vsrepo/vsrepo-args.type.d.ts +9 -0
  65. package/dist/types/vsrepo/vsrepo-method.type.d.ts +4 -0
  66. package/dist/types/vsrepo/vsrepo-options.type.d.ts +34 -0
  67. package/dist/types/vsrepo/vsrepo-orm-types.type.d.ts +17 -0
  68. package/dist/types/vsrepo/vsrepo-pretty-where.type.d.ts +7 -0
  69. package/dist/types/vsrepo/vsrepo-query-options.type.d.ts +17 -0
  70. package/dist/types/vsrepo/vsrepo-query-options.type.js +2 -0
  71. package/dist/types/vsrepo/vsrepo-query.type.d.ts +5 -0
  72. package/dist/types/vsrepo/vsrepo-query.type.js +2 -0
  73. package/dist/types/vsrepo/vsrepo-relations.type.d.ts +26 -0
  74. package/dist/types/vsrepo/vsrepo-relations.type.js +2 -0
  75. package/dist/types/vsrepo/vsrepo-resolve-args-data.type.d.ts +19 -0
  76. package/dist/types/vsrepo/vsrepo-resolve-args-data.type.js +2 -0
  77. package/dist/types/vsrepo/vsrepo-select.type.d.ts +15 -0
  78. package/dist/types/vsrepo/vsrepo-select.type.js +2 -0
  79. package/dist/types/vsrepo/vsrepo-transaction-options.type.d.ts +12 -0
  80. package/dist/types/vsrepo/vsrepo-transaction-options.type.js +2 -0
  81. package/dist/types/vsrepo/vsrepo-ugly-where.type.d.ts +9 -0
  82. package/dist/types/vsrepo/vsrepo-ugly-where.type.js +2 -0
  83. package/dist/types/vsrepo/vsrepo-where.type.d.ts +99 -0
  84. package/dist/types/vsrepo/vsrepo-where.type.js +2 -0
  85. package/package.json +16 -37
  86. package/README-DynamicRepo.md +0 -625
  87. package/README-DynamicRepo.pt-BR.md +0 -625
  88. package/dist/DynamicRepository.d.ts +0 -497
  89. package/dist/DynamicRepository.js +0 -26
  90. package/dist/VSRepoError.d.ts +0 -83
  91. package/dist/VSRepoError.js +0 -17
  92. package/dist/internal/decorators/dynamic-method.decorator.js +0 -14
  93. package/dist/internal/decorators/query-method.decorator.js +0 -20
  94. package/dist/internal/entities/dynamic-method-metadata.entity.js +0 -26
  95. package/dist/internal/errors/vs-repo.error.js +0 -31
  96. package/dist/internal/resolvers/base-methods.resolve.js +0 -536
  97. package/dist/internal/resolvers/create-update-payloads-with-relations.resolve.js +0 -143
  98. package/dist/internal/resolvers/data-payload-with-relations.resolve.js +0 -60
  99. package/dist/internal/resolvers/dbAndPrismaArgs.resolve.js +0 -63
  100. package/dist/internal/resolvers/dynamic-method-customization.resolve.js +0 -57
  101. package/dist/internal/resolvers/dynamic-method-info.resolve.js +0 -279
  102. package/dist/internal/resolvers/dynamic-methods-metadata.resolve.js +0 -15
  103. package/dist/internal/resolvers/merge-wheres.resolve.js +0 -22
  104. package/dist/internal/resolvers/pretty-wheres.resolve.js +0 -87
  105. package/dist/internal/resolvers/select.resolve.js +0 -7
  106. package/dist/internal/resolvers/specific-where.resolve.js +0 -84
  107. package/dist/internal/resolvers/ugly-where.resolve.js +0 -178
  108. package/dist/internal/utils/logger.util.js +0 -21
  109. package/dist/internal/utils/schemas.util.js +0 -31
  110. package/dist/internal/validation/build-config.validate.js +0 -84
  111. package/dist/internal/validation/constructor-config.validate.js +0 -64
  112. package/dist/internal/validation/dynamic-method-config.validate.js +0 -19
  113. package/dist/internal/validation/extension.validate.js +0 -15
  114. package/dist/internal/validation/is-object.validate.js +0 -6
  115. package/dist/internal/validation/method-options.validate.js +0 -42
  116. package/dist/internal/validation/obj-with-relations.validate.js +0 -37
  117. package/dist/internal/validation/prisma-client.validate.js +0 -10
  118. package/dist/internal/validation/query-method-arg.validate.js +0 -22
  119. package/dist/internal/validation/query-method-options.validate.js +0 -24
  120. package/scripts/configure-prisma-import.mjs +0 -283
  121. package/scripts/copy-types.mjs +0 -24
  122. /package/dist/{internal/decorators/types/dynamic-method-config.type.js → types/adapter/adapter-method-options.type.js} +0 -0
  123. /package/dist/{internal/errors/types/vs-repo-error-type.type.js → types/adapter/adapter-query-options.type.js} +0 -0
  124. /package/dist/{internal/errors/types/vs-repo-runtime-error-code.type.js → types/decorators/dynamic-method-options.type.js} +0 -0
  125. /package/dist/{internal/validation/types → types/decorators}/query-method-options.type.js +0 -0
  126. /package/dist/{internal/resolvers/types → types/dynamic-methods}/dynamic-method-customization.type.js +0 -0
  127. /package/dist/{internal/resolvers/types → types/dynamic-methods}/dynamic-method-info.type.js +0 -0
  128. /package/dist/{internal/resolvers/types → types/dynamic-methods}/dynamic-method-where-ops.type.js +0 -0
  129. /package/dist/{internal/resolvers/types/base-method-function.type.js → types/utils/count-result.type.js} +0 -0
  130. /package/dist/{internal/resolvers/types/pretty-where.type.js → types/utils/deep-partial.type.js} +0 -0
  131. /package/dist/{internal/resolvers/types/prisma-args.type.js → types/utils/keys-of-type.type.js} +0 -0
  132. /package/dist/{internal/resolvers/types/repository-build-instance.type.js → types/utils/methods-options.type.js} +0 -0
  133. /package/dist/{internal/resolvers/types/resolve-db-and-prisma-args-data.type.js → types/utils/ordering.type.js} +0 -0
  134. /package/dist/{internal/validation/types → types/utils}/pagination.type.js +0 -0
  135. /package/dist/{internal/resolvers/types/ugly-where.type.js → types/utils/perform-data.type.js} +0 -0
  136. /package/dist/{internal/validation/types/base-methods.type.js → types/utils/primitive.type.js} +0 -0
  137. /package/dist/{internal/validation/types → types/utils}/query-method-arg.type.js +0 -0
  138. /package/dist/{internal/validation/types → types/utils}/see-mode.type.js +0 -0
  139. /package/dist/{internal/validation/types/build-config.type.js → types/vsrepo/vsrepo-args.type.js} +0 -0
  140. /package/dist/{internal/validation/types/constructor-config.type.js → types/vsrepo/vsrepo-method.type.js} +0 -0
  141. /package/dist/{internal/validation/types/method-options.type.js → types/vsrepo/vsrepo-options.type.js} +0 -0
  142. /package/dist/{internal/validation/types/method.type.js → types/vsrepo/vsrepo-orm-types.type.js} +0 -0
  143. /package/dist/{internal/validation/types/relation.type.js → types/vsrepo/vsrepo-pretty-where.type.js} +0 -0
@@ -1,245 +1,281 @@
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) {
106
+ const optionsChecked = methodName === "getAll"
107
+ ? this.validator.validateGetAllMethodOptions(optionsUnchecked)
108
+ : this.validator.validateMethodOptions(optionsUnchecked);
109
+ optionsChecked.db ??= this.getDbClient();
110
+ const start = this.logger.startPerformLog("run " + methodName);
111
+ try {
112
+ const result = await fn(optionsChecked);
113
+ this.logger.endPerformLog(start);
114
+ return result;
115
+ }
116
+ catch (err) {
117
+ this.logger.endPerformLog(start);
118
+ // this.logger.logError(`Failed to run '${methodName}' on ${this.constructor.name}`, err);
119
+ throw err;
120
+ }
121
+ }
122
+ /**
123
+ * Runs `fn` inside a native transaction of the underlying ORM, sharing the
124
+ * transaction client (`tx`) across every repository call made within it.
125
+ */
126
+ async transaction(fn, options) {
127
+ if (typeof fn !== "function") {
128
+ this.fail("'fn' must be a valid function", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
129
+ }
130
+ return this.adapter.runInTransaction(fn, this.validator.validateTransactionOptions(options));
131
+ }
132
+ /** Returns the underlying ORM client instance used outside of transactions. */
133
+ getDbClient() {
134
+ return this.adapter.getDbClient();
135
+ }
136
+ /**
137
+ * Executes a raw query/statement directly against the underlying database.
138
+ *
139
+ * Use `$1`, `$2`, ... placeholders for values passed via `options.args` —
140
+ * never interpolate values directly into `query`, to avoid SQL injection.
141
+ * Set `options.modifying: true` for `INSERT`/`UPDATE`/`DELETE` statements.
142
+ *
143
+ * @example
144
+ * ```typescript
145
+ * const users = await userRepository.query<User[]>(
146
+ * 'SELECT * FROM "user" WHERE email = $1',
147
+ * { args: ["joao@email.com"] },
148
+ * );
149
+ *
150
+ * const affected = await userRepository.query<number>(
151
+ * 'UPDATE "user" SET active = true WHERE id = $1',
152
+ * { args: ["123"], modifying: true },
153
+ * );
154
+ * ```
155
+ */
156
+ async query(query, options) {
157
+ if (typeof query !== "string") {
158
+ this.fail("'query' must be a valid string", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
159
+ }
160
+ const optionsValidated = this.validator.validateQueryOptions(options);
161
+ optionsValidated.db ??= this.getDbClient();
162
+ const start = this.logger.startPerformLog("run query");
163
+ try {
164
+ const result = await this.adapter.query(query, {
165
+ args: optionsValidated.args,
166
+ db: optionsValidated.db,
167
+ modifying: optionsValidated.modifying ?? false,
168
+ });
169
+ this.logger.endPerformLog(start);
170
+ return result;
171
+ }
172
+ catch (err) {
173
+ this.logger.endPerformLog(start);
174
+ // this.logger.logError(`Failed to run 'query' on ${this.constructor.name}`, err);
175
+ throw err;
176
+ }
177
+ }
178
+ /** Fetches a record by its primary key (PK). */
179
+ async get(pk, options) {
180
+ return this.execBaseMethod(opt => this.adapter.findOne(this.mergeWheresResolver.resolve(opt.see, this.wherePk(pk)), opt), "get", options);
181
+ }
182
+ /** Fetches a record by PK and throws an Error if not found. */
183
+ async getOrThrow(pk, options) {
184
+ return this.execBaseMethod(opt => this.adapter.findOneOrThrow(this.mergeWheresResolver.resolve(opt.see, this.wherePk(pk)), opt), "getOrThrow", options);
185
+ }
186
+ /** Fetches multiple records by a list of primary keys (PKs). */
187
+ async getList(pks, options) {
188
+ if (!Array.isArray(pks)) {
189
+ this.fail("'pks' must be a valid array", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
190
+ }
191
+ return this.execBaseMethod(opt => this.adapter.findMany(this.mergeWheresResolver.resolve(opt.see, this.wherePkIn(pks)), opt), "getList", options);
192
+ }
193
+ /** Fetches all records (respects the repository's `defaultOrdering` unless `order` is provided). */
194
+ async getAll(options) {
195
+ return this.execBaseMethod((opt) => this.adapter.findMany(this.mergeWheresResolver.resolve(opt.see, {}), {
196
+ ...opt,
197
+ order: opt.order ?? this.defaultOrdering,
198
+ }), "getAll", options);
199
+ }
200
+ /** Creates or updates (upsert) a record. */
201
+ async save(obj, options) {
202
+ return this.execBaseMethod(opt => this.adapter.save(obj, opt), "save", options);
203
+ }
204
+ /** Creates or updates (upsert) multiple records in a single operation. */
205
+ async saveList(objs, options) {
206
+ if (!Array.isArray(objs)) {
207
+ this.fail("'objs' must be a valid array", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
208
+ }
209
+ return this.execBaseMethod(opt => this.adapter.saveMany(objs, opt), "saveList", options);
210
+ }
211
+ /** Deletes a record identified by its primary key (PK). */
212
+ async remove(pk, options) {
213
+ return this.execBaseMethod(opt => this.adapter.delete(this.mergeWheresResolver.resolve(opt.see, this.wherePk(pk)), opt), "remove", options);
214
+ }
215
+ /** Deletes multiple records by their primary keys, returning the count of affected rows. */
216
+ async removeList(pks, options) {
217
+ if (!Array.isArray(pks)) {
218
+ this.fail("'pks' must be a valid array", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
219
+ }
220
+ return this.execBaseMethod(opt => this.adapter.deleteMany(this.mergeWheresResolver.resolve(opt.see, this.wherePkIn(pks)), opt), "removeList", options);
221
+ }
222
+ /** Partially updates an existing record by its primary key (PK). */
223
+ async patch(pk, obj, options) {
224
+ return this.execBaseMethod(opt => this.adapter.update(this.mergeWheresResolver.resolve(opt.see, this.wherePk(pk)), obj, opt), "patch", options);
225
+ }
226
+ /**
227
+ * Fetches a record by PK and returns it deep-merged, in memory, with the
228
+ * provided object — does **not** persist anything (pass the result to
229
+ * `save`/`patch` yourself to write it).
230
+ */
231
+ async merge(pk, obj, options) {
232
+ return this.execBaseMethod(opt => this.adapter.merge(this.mergeWheresResolver.resolve(opt.see, this.wherePk(pk)), obj, opt), "merge", options);
233
+ }
234
+ /** Returns the total number of records. */
235
+ async total(options) {
236
+ return this.execBaseMethod(opt => this.adapter.count(this.mergeWheresResolver.resolve(opt.see, {}), opt), "total", options);
237
+ }
238
+ /** Checks whether a record exists by its primary key (PK). */
239
+ async has(pk, options) {
240
+ return this.execBaseMethod(opt => this.adapter.exists(this.mergeWheresResolver.resolve(opt.see, this.wherePk(pk)), opt), "has", options);
241
+ }
242
+ /** Marks a record as deleted (soft-delete). Requires `softRemoveKey` to be configured on the repository. */
243
+ async softRemove(pk, options) {
244
+ if (!this.softRemoveKey) {
245
+ this.fail("this method can only be used if you have configured 'softRemoveKey' in this repository.", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
246
+ }
247
+ const key = this.softRemoveKey;
248
+ return this.execBaseMethod(opt => this.adapter.update(this.mergeWheresResolver.resolve(opt.see ?? "all", this.wherePk(pk)), { [key]: new Date() }, opt), "softRemove", options);
249
+ }
250
+ /** Marks multiple records as deleted (soft-delete) in batch. Requires `softRemoveKey` to be configured on the repository. */
251
+ async softRemoveList(pks, options) {
252
+ if (!this.softRemoveKey) {
253
+ this.fail("this method can only be used if you have configured 'softRemoveKey' in this repository.", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
254
+ }
255
+ if (!Array.isArray(pks)) {
256
+ this.fail("'pks' must be a valid array", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
257
+ }
258
+ const key = this.softRemoveKey;
259
+ return this.execBaseMethod(opt => this.adapter.updateMany(this.mergeWheresResolver.resolve(opt.see ?? "all", this.wherePkIn(pks)), { [key]: new Date() }, opt), "softRemoveList", options);
260
+ }
261
+ /** Restores a record previously marked as deleted (soft-delete). Requires `softRemoveKey` to be configured on the repository. */
262
+ async restore(pk, options) {
263
+ if (!this.softRemoveKey) {
264
+ this.fail("this method can only be used if you have configured 'softRemoveKey' in this repository.", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
265
+ }
266
+ const key = this.softRemoveKey;
267
+ return this.execBaseMethod(opt => this.adapter.update(this.mergeWheresResolver.resolve(opt.see ?? "all", this.wherePk(pk)), { [key]: null }, opt), "restore", options);
268
+ }
269
+ /** Restores multiple records previously marked as deleted (soft-delete) in batch. Requires `softRemoveKey` to be configured on the repository. */
270
+ async restoreList(pks, options) {
271
+ if (!this.softRemoveKey) {
272
+ this.fail("this method can only be used if you have configured 'softRemoveKey' in this repository.", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
273
+ }
274
+ if (!Array.isArray(pks)) {
275
+ this.fail("'pks' must be a valid array", vsrepo_error_type_enum_1.VSRepoErrorType.BASE);
276
+ }
277
+ const key = this.softRemoveKey;
278
+ return this.execBaseMethod(opt => this.adapter.updateMany(this.mergeWheresResolver.resolve(opt.see ?? "all", this.wherePkIn(pks)), { [key]: null }, opt), "restoreList", options);
241
279
  }
242
280
  }
243
281
  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;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DynamicMethod = DynamicMethod;
4
+ const dynamic_methods_key_constant_1 = require("../internal/constants/dynamic-methods-key.constant");
5
+ const decorators_validator_1 = require("../internal/validators/decorators.validator");
6
+ /**
7
+ * Property decorator used to declare a dynamic method on a `VSRepository` subclass.
8
+ *
9
+ * Applied to a `declare` class field whose name follows one of the supported
10
+ * dynamic-method patterns (e.g. `findByEmail`, `findManyByStatusPaginated`,
11
+ * `upsertById`), the method's behavior is inferred from the field name at
12
+ * construction time, optionally adjusted via `options`.
13
+ *
14
+ * @template T Entity type the decorated method operates on.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * class UserRepository extends VSRepository<User, string> {
19
+ * @DynamicMethod()
20
+ * declare findByEmail: (email: string) => Promise<User[]>;
21
+ *
22
+ * @DynamicMethod<User>({ injectOrdering: { createdAt: "desc" } })
23
+ * declare findByAge: (age: number) => Promise<User[]>;
24
+ * }
25
+ * ```
26
+ *
27
+ * @publicApi
28
+ */
29
+ function DynamicMethod(options) {
30
+ const validatedOptions = options
31
+ ? decorators_validator_1.DecoratorsValidator.validateDynamicMethodOptions(options)
32
+ : undefined;
33
+ return (target, propertyKey) => {
34
+ const methods = Reflect.getMetadata(dynamic_methods_key_constant_1.DYNAMIC_METHODS_KEY, target) ?? [];
35
+ methods.push({ ...validatedOptions, propertyKey });
36
+ Reflect.defineMetadata(dynamic_methods_key_constant_1.DYNAMIC_METHODS_KEY, methods, target);
37
+ };
38
+ }
@@ -0,0 +1,27 @@
1
+ import { QueryMethodOptions } from "../types/decorators/query-method-options.type";
2
+ /**
3
+ * Property decorator used to declare a raw SQL query method on a `VSRepository`
4
+ * subclass, bypassing name-based method parsing entirely.
5
+ *
6
+ * Applied to a `declare` class field, it executes `value` directly through the
7
+ * adapter's `query()` method, with parameters injected positionally via the
8
+ * `args` array passed at the call site (`$1`, `$2`, ... placeholders).
9
+ *
10
+ * @param value Raw SQL statement to execute. Use `$1`, `$2`, ... placeholders for
11
+ * the values that will be passed via `args` — never interpolate values directly into `value`.
12
+ * @param options Optional configuration; set `modifying: true` for `INSERT`/`UPDATE`/`DELETE` statements.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * class UserRepository extends VSRepository<User, string> {
17
+ * @QueryMethod('SELECT * FROM "user" WHERE email = $1')
18
+ * declare findByEmailRaw: (arg: QueryMethodArg<[email: string]>) => Promise<User[]>;
19
+ *
20
+ * @QueryMethod('UPDATE "user" SET active = true WHERE id = $1', { modifying: true })
21
+ * declare activateUser: (arg: QueryMethodArg<[id: string]>) => Promise<number>;
22
+ * }
23
+ * ```
24
+ *
25
+ * @publicApi
26
+ */
27
+ export declare function QueryMethod(value: string, options?: QueryMethodOptions): PropertyDecorator;