metal-orm 1.1.10 → 1.1.16

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 (192) hide show
  1. package/README.md +758 -769
  2. package/dist/index.cjs +106 -43
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +5 -5
  5. package/dist/index.d.ts +5 -5
  6. package/dist/index.js +106 -43
  7. package/dist/index.js.map +1 -1
  8. package/package.json +100 -96
  9. package/scripts/generate-entities/render.mjs +21 -12
  10. package/scripts/generate-entities/schema.mjs +209 -195
  11. package/scripts/generate-entities/tree-detection.mjs +159 -153
  12. package/scripts/inflection/pt-br.mjs +468 -468
  13. package/scripts/run-eslint.mjs +33 -34
  14. package/scripts/show-sql-seed.sql +102 -0
  15. package/scripts/show-sql.mjs +321 -325
  16. package/src/bulk/bulk-delete-executor.ts +3 -5
  17. package/src/bulk/bulk-insert-executor.ts +7 -7
  18. package/src/bulk/bulk-update-executor.ts +2 -2
  19. package/src/bulk/bulk-upsert-executor.ts +5 -7
  20. package/src/codegen/naming-strategy.ts +54 -54
  21. package/src/codegen/typescript.ts +482 -482
  22. package/src/core/ast/adapters.ts +1 -1
  23. package/src/core/ast/builders.ts +123 -123
  24. package/src/core/ast/expression-builders.ts +745 -745
  25. package/src/core/ast/expression-nodes.ts +304 -304
  26. package/src/core/ast/expression-visitor.ts +212 -212
  27. package/src/core/ast/expression.ts +20 -20
  28. package/src/core/ast/helpers.ts +14 -14
  29. package/src/core/ast/join.ts +18 -18
  30. package/src/core/ast/query.ts +200 -200
  31. package/src/core/ddl/dialects/base-schema-dialect.ts +1 -1
  32. package/src/core/ddl/dialects/mssql-schema-dialect.ts +1 -1
  33. package/src/core/ddl/dialects/mysql-schema-dialect.ts +1 -1
  34. package/src/core/ddl/dialects/postgres-schema-dialect.ts +1 -1
  35. package/src/core/ddl/dialects/render-reference.test.ts +70 -70
  36. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -1
  37. package/src/core/ddl/introspect/catalogs/index.ts +5 -5
  38. package/src/core/ddl/introspect/catalogs/postgres.ts +146 -146
  39. package/src/core/ddl/introspect/context.ts +15 -15
  40. package/src/core/ddl/introspect/functions/postgres.ts +35 -35
  41. package/src/core/ddl/introspect/mysql.ts +113 -36
  42. package/src/core/ddl/introspect/registry.ts +40 -40
  43. package/src/core/ddl/introspect/run-select.ts +35 -35
  44. package/src/core/ddl/introspect/utils.ts +56 -56
  45. package/src/core/ddl/naming-strategy.ts +16 -16
  46. package/src/core/ddl/schema-dialect.ts +56 -56
  47. package/src/core/ddl/schema-diff.ts +234 -234
  48. package/src/core/ddl/schema-generator.ts +1 -1
  49. package/src/core/ddl/schema-introspect.ts +25 -25
  50. package/src/core/ddl/sql-writing.ts +171 -171
  51. package/src/core/dialect/abstract.ts +541 -541
  52. package/src/core/dialect/base/cte-compiler.ts +33 -33
  53. package/src/core/dialect/base/function-table-formatter.ts +103 -103
  54. package/src/core/dialect/base/groupby-compiler.ts +21 -21
  55. package/src/core/dialect/base/join-compiler.ts +25 -25
  56. package/src/core/dialect/base/orderby-compiler.ts +35 -35
  57. package/src/core/dialect/base/pagination-strategy.ts +32 -32
  58. package/src/core/dialect/base/returning-strategy.ts +57 -57
  59. package/src/core/dialect/base/sql-dialect.ts +294 -294
  60. package/src/core/dialect/dialect-factory.ts +91 -91
  61. package/src/core/dialect/mssql/functions.ts +120 -120
  62. package/src/core/dialect/mssql/index.ts +317 -317
  63. package/src/core/dialect/mysql/functions.ts +103 -103
  64. package/src/core/dialect/mysql/index.ts +105 -105
  65. package/src/core/dialect/postgres/functions.ts +108 -108
  66. package/src/core/dialect/postgres/index.ts +91 -91
  67. package/src/core/dialect/sqlite/functions.ts +129 -129
  68. package/src/core/dialect/sqlite/index.ts +67 -67
  69. package/src/core/driver/database-driver.ts +19 -19
  70. package/src/core/driver/mssql-driver.ts +23 -23
  71. package/src/core/driver/mysql-driver.ts +23 -23
  72. package/src/core/driver/postgres-driver.ts +23 -23
  73. package/src/core/driver/sqlite-driver.ts +23 -23
  74. package/src/core/execution/db-executor.ts +92 -92
  75. package/src/core/execution/executors/better-sqlite3-executor.ts +94 -94
  76. package/src/core/execution/executors/mssql-executor.ts +184 -184
  77. package/src/core/execution/executors/mysql-executor.ts +88 -88
  78. package/src/core/execution/executors/postgres-executor.ts +26 -26
  79. package/src/core/execution/executors/sqlite-executor.ts +42 -42
  80. package/src/core/execution/pooling/pool-types.ts +30 -30
  81. package/src/core/execution/pooling/pool.ts +275 -275
  82. package/src/core/functions/array.ts +35 -35
  83. package/src/core/functions/control-flow.ts +79 -79
  84. package/src/core/functions/datetime.ts +237 -237
  85. package/src/core/functions/json.ts +70 -70
  86. package/src/core/functions/numeric.ts +297 -297
  87. package/src/core/functions/text.ts +334 -334
  88. package/src/core/functions/types.ts +33 -33
  89. package/src/core/hydration/types.ts +51 -51
  90. package/src/core/sql/sql-operator-config.ts +39 -39
  91. package/src/core/sql/sql.ts +152 -152
  92. package/src/decorators/column-decorator.ts +87 -87
  93. package/src/decorators/decorator-metadata.ts +143 -143
  94. package/src/decorators/entity.ts +93 -93
  95. package/src/decorators/index.ts +39 -39
  96. package/src/decorators/relations.ts +231 -231
  97. package/src/decorators/transformers/built-in/string-transformers.ts +231 -231
  98. package/src/decorators/transformers/transformer-decorators.ts +134 -134
  99. package/src/decorators/transformers/transformer-executor.ts +171 -171
  100. package/src/decorators/transformers/transformer-metadata.ts +73 -73
  101. package/src/decorators/validators/built-in/br-cep-validator.ts +55 -55
  102. package/src/decorators/validators/built-in/br-cnpj-validator.ts +105 -105
  103. package/src/decorators/validators/built-in/br-cpf-validator.ts +103 -103
  104. package/src/decorators/validators/country-validator-registry.ts +64 -64
  105. package/src/decorators/validators/country-validators-decorators.ts +203 -203
  106. package/src/decorators/validators/country-validators.ts +105 -105
  107. package/src/decorators/validators/validator-metadata.ts +59 -59
  108. package/src/dto/dto-types.ts +226 -226
  109. package/src/dto/filter-types.ts +141 -141
  110. package/src/dto/index.ts +95 -95
  111. package/src/dto/openapi/generators/base.ts +29 -29
  112. package/src/dto/openapi/generators/column.ts +37 -37
  113. package/src/dto/openapi/generators/dto.ts +91 -91
  114. package/src/dto/openapi/generators/filter.ts +75 -75
  115. package/src/dto/openapi/generators/nested-dto.ts +618 -618
  116. package/src/dto/openapi/generators/pagination.ts +111 -111
  117. package/src/dto/openapi/generators/relation-filter.ts +228 -228
  118. package/src/dto/openapi/index.ts +12 -12
  119. package/src/dto/openapi/types.ts +101 -101
  120. package/src/dto/openapi/utilities.ts +95 -95
  121. package/src/dto/pagination-utils.ts +150 -150
  122. package/src/dto/transform.ts +197 -197
  123. package/src/index.ts +86 -86
  124. package/src/orm/als.ts +58 -58
  125. package/src/orm/domain-event-bus.ts +119 -119
  126. package/src/orm/entity-context.ts +86 -86
  127. package/src/orm/entity-materializer.ts +159 -159
  128. package/src/orm/entity-meta.ts +97 -97
  129. package/src/orm/entity-registry.ts +39 -39
  130. package/src/orm/entity-relation-cache.ts +39 -39
  131. package/src/orm/execute.ts +194 -194
  132. package/src/orm/execution-context.ts +18 -18
  133. package/src/orm/hydration-context.ts +26 -26
  134. package/src/orm/hydration.ts +112 -112
  135. package/src/orm/identity-map.ts +61 -61
  136. package/src/orm/interceptor-pipeline.ts +16 -16
  137. package/src/orm/lazy-batch/belongs-to-many.ts +119 -119
  138. package/src/orm/lazy-batch/belongs-to.ts +108 -108
  139. package/src/orm/lazy-batch/has-many.ts +69 -69
  140. package/src/orm/lazy-batch/has-one.ts +68 -68
  141. package/src/orm/lazy-batch/shared.ts +125 -125
  142. package/src/orm/orm-session.ts +620 -620
  143. package/src/orm/orm.ts +34 -34
  144. package/src/orm/pooled-executor-factory.ts +121 -121
  145. package/src/orm/query-logger.ts +40 -40
  146. package/src/orm/relation-change-processor.ts +420 -420
  147. package/src/orm/relations/belongs-to.ts +143 -143
  148. package/src/orm/relations/has-many.ts +204 -204
  149. package/src/orm/relations/has-one.ts +174 -174
  150. package/src/orm/relations/many-to-many.ts +288 -288
  151. package/src/orm/relations/morph-many.ts +156 -156
  152. package/src/orm/relations/morph-one.ts +151 -151
  153. package/src/orm/relations/morph-to.ts +162 -162
  154. package/src/orm/runtime-types.ts +92 -92
  155. package/src/orm/save-graph.ts +561 -561
  156. package/src/orm/transaction-runner.ts +24 -24
  157. package/src/orm/unit-of-work.ts +426 -426
  158. package/src/query/index.ts +69 -69
  159. package/src/query/target.ts +46 -46
  160. package/src/query-builder/column-selector.ts +81 -81
  161. package/src/query-builder/delete.ts +141 -141
  162. package/src/query-builder/insert-query-state.ts +149 -149
  163. package/src/query-builder/insert.ts +117 -117
  164. package/src/query-builder/query-ast-service.ts +287 -287
  165. package/src/query-builder/query-resolution.ts +78 -78
  166. package/src/query-builder/raw-column-parser.ts +38 -38
  167. package/src/query-builder/relation-filter-utils.ts +153 -153
  168. package/src/query-builder/relation-join-planner.ts +10 -10
  169. package/src/query-builder/relation-manager.ts +87 -87
  170. package/src/query-builder/relation-projection-helper.ts +102 -102
  171. package/src/query-builder/relation-types.ts +9 -9
  172. package/src/query-builder/relation-utils.ts +15 -15
  173. package/src/query-builder/select/cte-facet.ts +40 -40
  174. package/src/query-builder/select/from-facet.ts +80 -80
  175. package/src/query-builder/select/join-facet.ts +62 -62
  176. package/src/query-builder/select/predicate-facet.ts +104 -104
  177. package/src/query-builder/select/projection-facet.ts +70 -70
  178. package/src/query-builder/select/relation-facet.ts +81 -81
  179. package/src/query-builder/select/setop-facet.ts +36 -36
  180. package/src/query-builder/select-helpers.ts +1 -1
  181. package/src/query-builder/select-query-builder-deps.ts +130 -130
  182. package/src/query-builder/select-query-state.ts +207 -207
  183. package/src/query-builder/update.ts +151 -151
  184. package/src/schema/column-types.ts +322 -322
  185. package/src/schema/table-guards.ts +37 -37
  186. package/src/schema/table.ts +281 -281
  187. package/src/tree/index.ts +13 -13
  188. package/src/tree/nested-set-strategy.ts +545 -545
  189. package/src/tree/tree-decorator.ts +397 -397
  190. package/src/tree/tree-manager.ts +754 -754
  191. package/src/tree/tree-query.ts +427 -427
  192. package/src/tree/tree-types.ts +299 -299
@@ -1,134 +1,134 @@
1
- import { resolveFieldDecoratorInfo } from '../decorator-metadata.js';
2
- import type { TransformerMetadata } from './transformer-metadata.js';
3
- import {
4
- TrimTransformer,
5
- CaseTransformer,
6
- AlphanumericValidator,
7
- EmailValidator,
8
- LengthValidator,
9
- PatternValidator
10
- } from './built-in/string-transformers.js';
11
-
12
- const registerTransformerMetadata = (
13
- targetOrValue: unknown,
14
- contextOrProperty: unknown,
15
- metadata: Partial<TransformerMetadata>
16
- ): void => {
17
- const { propertyName, bag } = resolveFieldDecoratorInfo(
18
- targetOrValue,
19
- contextOrProperty,
20
- 'Transformer'
21
- );
22
-
23
- // Find existing transformer metadata for this property
24
- let existing = bag.transformers.find(t => t.propertyName === propertyName);
25
-
26
- if (!existing) {
27
- existing = {
28
- propertyName,
29
- metadata: {
30
- propertyName,
31
- transformers: [],
32
- validators: [],
33
- sanitizers: [],
34
- executionOrder: 'both'
35
- }
36
- };
37
- bag.transformers.push(existing);
38
- }
39
-
40
- // Merge metadata
41
- if (metadata.transformers) {
42
- existing.metadata.transformers.push(...metadata.transformers);
43
- }
44
- if (metadata.validators) {
45
- existing.metadata.validators.push(...metadata.validators);
46
- }
47
- if (metadata.sanitizers) {
48
- existing.metadata.sanitizers.push(...metadata.sanitizers);
49
- }
50
- if (metadata.executionOrder) {
51
- existing.metadata.executionOrder = metadata.executionOrder;
52
- }
53
- };
54
-
55
- // Trim decorator
56
- export function Trim(options?: { trimStart?: boolean; trimEnd?: boolean; trimAll?: boolean }) {
57
- return function (targetOrValue: unknown, contextOrProperty: unknown) {
58
- registerTransformerMetadata(targetOrValue, contextOrProperty, {
59
- sanitizers: [new TrimTransformer(options)]
60
- });
61
- };
62
- }
63
-
64
- // Lower case decorator
65
- export function Lower() {
66
- return function (targetOrValue: unknown, contextOrProperty: unknown) {
67
- registerTransformerMetadata(targetOrValue, contextOrProperty, {
68
- sanitizers: [new CaseTransformer('lower')]
69
- });
70
- };
71
- }
72
-
73
- // Upper case decorator
74
- export function Upper() {
75
- return function (targetOrValue: unknown, contextOrProperty: unknown) {
76
- registerTransformerMetadata(targetOrValue, contextOrProperty, {
77
- sanitizers: [new CaseTransformer('upper')]
78
- });
79
- };
80
- }
81
-
82
- // Capitalize decorator
83
- export function Capitalize() {
84
- return function (targetOrValue: unknown, contextOrProperty: unknown) {
85
- registerTransformerMetadata(targetOrValue, contextOrProperty, {
86
- sanitizers: [new CaseTransformer('capitalize')]
87
- });
88
- };
89
- }
90
-
91
- // Title case decorator
92
- export function Title() {
93
- return function (targetOrValue: unknown, contextOrProperty: unknown) {
94
- registerTransformerMetadata(targetOrValue, contextOrProperty, {
95
- sanitizers: [new CaseTransformer('title')]
96
- });
97
- };
98
- }
99
-
100
- // Alphanumeric decorator
101
- export function Alphanumeric(options?: { allowSpaces?: boolean; allowUnderscores?: boolean; allowHyphens?: boolean }) {
102
- return function (targetOrValue: unknown, contextOrProperty: unknown) {
103
- registerTransformerMetadata(targetOrValue, contextOrProperty, {
104
- validators: [new AlphanumericValidator(options)]
105
- });
106
- };
107
- }
108
-
109
- // Email decorator
110
- export function Email(options?: { allowPlus?: boolean; requireTLD?: boolean }) {
111
- return function (targetOrValue: unknown, contextOrProperty: unknown) {
112
- registerTransformerMetadata(targetOrValue, contextOrProperty, {
113
- validators: [new EmailValidator(options)]
114
- });
115
- };
116
- }
117
-
118
- // Length decorator
119
- export function Length(options: { min?: number; max?: number; exact?: number }) {
120
- return function (targetOrValue: unknown, contextOrProperty: unknown) {
121
- registerTransformerMetadata(targetOrValue, contextOrProperty, {
122
- validators: [new LengthValidator(options)]
123
- });
124
- };
125
- }
126
-
127
- // Pattern decorator
128
- export function Pattern(options: { pattern: RegExp; flags?: string; errorMessage?: string; replacement?: string }) {
129
- return function (targetOrValue: unknown, contextOrProperty: unknown) {
130
- registerTransformerMetadata(targetOrValue, contextOrProperty, {
131
- validators: [new PatternValidator(options)]
132
- });
133
- };
134
- }
1
+ import { resolveFieldDecoratorInfo } from '../decorator-metadata.js';
2
+ import type { TransformerMetadata } from './transformer-metadata.js';
3
+ import {
4
+ TrimTransformer,
5
+ CaseTransformer,
6
+ AlphanumericValidator,
7
+ EmailValidator,
8
+ LengthValidator,
9
+ PatternValidator
10
+ } from './built-in/string-transformers.js';
11
+
12
+ const registerTransformerMetadata = (
13
+ targetOrValue: unknown,
14
+ contextOrProperty: unknown,
15
+ metadata: Partial<TransformerMetadata>
16
+ ): void => {
17
+ const { propertyName, bag } = resolveFieldDecoratorInfo(
18
+ targetOrValue,
19
+ contextOrProperty,
20
+ 'Transformer'
21
+ );
22
+
23
+ // Find existing transformer metadata for this property
24
+ let existing = bag.transformers.find(t => t.propertyName === propertyName);
25
+
26
+ if (!existing) {
27
+ existing = {
28
+ propertyName,
29
+ metadata: {
30
+ propertyName,
31
+ transformers: [],
32
+ validators: [],
33
+ sanitizers: [],
34
+ executionOrder: 'both'
35
+ }
36
+ };
37
+ bag.transformers.push(existing);
38
+ }
39
+
40
+ // Merge metadata
41
+ if (metadata.transformers) {
42
+ existing.metadata.transformers.push(...metadata.transformers);
43
+ }
44
+ if (metadata.validators) {
45
+ existing.metadata.validators.push(...metadata.validators);
46
+ }
47
+ if (metadata.sanitizers) {
48
+ existing.metadata.sanitizers.push(...metadata.sanitizers);
49
+ }
50
+ if (metadata.executionOrder) {
51
+ existing.metadata.executionOrder = metadata.executionOrder;
52
+ }
53
+ };
54
+
55
+ // Trim decorator
56
+ export function Trim(options?: { trimStart?: boolean; trimEnd?: boolean; trimAll?: boolean }) {
57
+ return function (targetOrValue: unknown, contextOrProperty: unknown) {
58
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
59
+ sanitizers: [new TrimTransformer(options)]
60
+ });
61
+ };
62
+ }
63
+
64
+ // Lower case decorator
65
+ export function Lower() {
66
+ return function (targetOrValue: unknown, contextOrProperty: unknown) {
67
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
68
+ sanitizers: [new CaseTransformer('lower')]
69
+ });
70
+ };
71
+ }
72
+
73
+ // Upper case decorator
74
+ export function Upper() {
75
+ return function (targetOrValue: unknown, contextOrProperty: unknown) {
76
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
77
+ sanitizers: [new CaseTransformer('upper')]
78
+ });
79
+ };
80
+ }
81
+
82
+ // Capitalize decorator
83
+ export function Capitalize() {
84
+ return function (targetOrValue: unknown, contextOrProperty: unknown) {
85
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
86
+ sanitizers: [new CaseTransformer('capitalize')]
87
+ });
88
+ };
89
+ }
90
+
91
+ // Title case decorator
92
+ export function Title() {
93
+ return function (targetOrValue: unknown, contextOrProperty: unknown) {
94
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
95
+ sanitizers: [new CaseTransformer('title')]
96
+ });
97
+ };
98
+ }
99
+
100
+ // Alphanumeric decorator
101
+ export function Alphanumeric(options?: { allowSpaces?: boolean; allowUnderscores?: boolean; allowHyphens?: boolean }) {
102
+ return function (targetOrValue: unknown, contextOrProperty: unknown) {
103
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
104
+ validators: [new AlphanumericValidator(options)]
105
+ });
106
+ };
107
+ }
108
+
109
+ // Email decorator
110
+ export function Email(options?: { allowPlus?: boolean; requireTLD?: boolean }) {
111
+ return function (targetOrValue: unknown, contextOrProperty: unknown) {
112
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
113
+ validators: [new EmailValidator(options)]
114
+ });
115
+ };
116
+ }
117
+
118
+ // Length decorator
119
+ export function Length(options: { min?: number; max?: number; exact?: number }) {
120
+ return function (targetOrValue: unknown, contextOrProperty: unknown) {
121
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
122
+ validators: [new LengthValidator(options)]
123
+ });
124
+ };
125
+ }
126
+
127
+ // Pattern decorator
128
+ export function Pattern(options: { pattern: RegExp; flags?: string; errorMessage?: string; replacement?: string }) {
129
+ return function (targetOrValue: unknown, contextOrProperty: unknown) {
130
+ registerTransformerMetadata(targetOrValue, contextOrProperty, {
131
+ validators: [new PatternValidator(options)]
132
+ });
133
+ };
134
+ }
@@ -1,171 +1,171 @@
1
- import { getEntityMetadata } from '../../orm/entity-metadata.js';
2
- import type { EntityConstructor, EntityMetadata } from '../../orm/entity-metadata.js';
3
- import type {
4
- TransformContext,
5
- TransformerConfig,
6
- PropertyTransformer,
7
- PropertyValidator,
8
- PropertySanitizer,
9
- TransformerMetadata
10
- } from './transformer-metadata.js';
11
-
12
- export class TransformerExecutor {
13
- private config: TransformerConfig = {
14
- auto: false,
15
- execute: 'both',
16
- stopOnFirstError: false
17
- };
18
-
19
- constructor(options?: TransformerConfig) {
20
- this.config = { ...this.config, ...options };
21
- }
22
-
23
- /**
24
- * Applies all transformers to an entity instance
25
- */
26
- async applyTransformers(entity: Record<string, unknown>, entityClass: EntityConstructor, context: Partial<TransformContext> = {}): Promise<void> {
27
- const meta = getEntityMetadata(entityClass);
28
- if (!meta || !meta.transformers) return;
29
-
30
- // Get column types from entity metadata
31
- const columnTypes = this.getColumnTypes(meta);
32
-
33
- for (const [propertyName, transformerMeta] of Object.entries(meta.transformers)) {
34
- const typedMeta = transformerMeta as unknown as TransformerMetadata;
35
-
36
- // Skip if transformer should not execute in current context
37
- if (!this.shouldExecute(typedMeta.executionOrder, context.isUpdate)) {
38
- continue;
39
- }
40
-
41
- // Get current value
42
- const value = entity[propertyName];
43
-
44
- // Skip if value is null or undefined
45
- if (value === null || value === undefined) continue;
46
-
47
- // Create transform context
48
- const transformContext: TransformContext = {
49
- entityName: entityClass.name,
50
- propertyName,
51
- columnType: columnTypes[propertyName] || 'VARCHAR',
52
- isUpdate: context.isUpdate || false,
53
- originalValue: context.originalValue,
54
- autoTransform: this.config.auto
55
- };
56
-
57
- try {
58
- // Apply sanitizers first
59
- let transformedValue = this.applySanitizers(value, typedMeta.sanitizers, transformContext);
60
-
61
- // Apply transformers
62
- transformedValue = this.applyTransformersToValue(transformedValue, typedMeta.transformers, transformContext);
63
-
64
- // Apply validation
65
- this.applyValidators(transformedValue, typedMeta.validators, transformContext);
66
-
67
- // Update the entity with the transformed value
68
- entity[propertyName] = transformedValue;
69
- } catch (_error) {
70
- // Handle errors based on config
71
- if (this.config.stopOnFirstError) {
72
- throw _error;
73
- }
74
- // Continue with other properties
75
- }
76
- }
77
- }
78
-
79
- private shouldExecute(executionOrder: 'before-save' | 'after-load' | 'both', isUpdate: boolean): boolean {
80
- if (executionOrder === 'both') return true;
81
- if (isUpdate && executionOrder === 'before-save') return true;
82
- if (!isUpdate && executionOrder === 'after-load') return true;
83
- return false;
84
- }
85
-
86
- private getColumnTypes(meta: EntityMetadata): Record<string, string> {
87
- if (!meta || !meta.columns) return {};
88
-
89
- const columnTypes: Record<string, string> = {};
90
- for (const [propertyName, columnDef] of Object.entries(meta.columns)) {
91
- columnTypes[propertyName] = columnDef.type || 'VARCHAR';
92
- }
93
- return columnTypes;
94
- }
95
-
96
- private applySanitizers(value: unknown, sanitizers: PropertySanitizer[], context: TransformContext): unknown {
97
- let result = value;
98
- for (const sanitizer of sanitizers) {
99
- try {
100
- result = sanitizer.sanitize(result, context);
101
- } catch (_error: unknown) {
102
- // Continue with next sanitizer if this one fails
103
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
104
- const error = _error;
105
- }
106
- }
107
- return result;
108
- }
109
-
110
- private applyTransformersToValue(value: unknown, transformers: PropertyTransformer[], context: TransformContext): unknown {
111
- let result = value;
112
- for (const transformer of transformers) {
113
- try {
114
- result = transformer.transform(result, context);
115
- } catch (_error: unknown) {
116
- // Continue with next transformer if this one fails
117
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
118
- const error = _error;
119
- }
120
- }
121
- return result;
122
- }
123
-
124
- private applyValidators(value: unknown, validators: PropertyValidator[], context: TransformContext): void {
125
- const errors: string[] = [];
126
-
127
- for (const validator of validators) {
128
- try {
129
- const result = validator.validate(value, context);
130
-
131
- if (!result.isValid) {
132
- if (context.autoTransform && 'autoTransform' in validator) {
133
- const autoResult = (validator as { autoTransform?: (v: unknown, c: TransformContext) => { success: boolean; correctedValue?: unknown; message?: string } }).autoTransform?.(value, context);
134
- if (typeof autoResult === 'object' && autoResult !== null && 'success' in autoResult && autoResult.success && 'correctedValue' in autoResult && autoResult.correctedValue !== undefined) {
135
- // Auto-correct the value
136
- (context as { correctedValue?: unknown }).correctedValue = autoResult.correctedValue;
137
- return; // Value is now valid
138
- }
139
- }
140
- errors.push(result.error || `Validation failed for ${validator.name}`);
141
-
142
- if (this.config.stopOnFirstError) {
143
- break;
144
- }
145
- }
146
- } catch (_error: unknown) {
147
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
148
- const error = _error;
149
- if (_error instanceof Error) {
150
- errors.push(_error.message);
151
- } else {
152
- errors.push('Validation failed');
153
- }
154
- if (this.config.stopOnFirstError) {
155
- break;
156
- }
157
- }
158
- }
159
-
160
- if (errors.length > 0) {
161
- throw new Error(errors.join(', '));
162
- }
163
- }
164
- }
165
-
166
- /**
167
- * Creates a transformer executor with specified config
168
- */
169
- export const createTransformerExecutor = (config?: TransformerConfig): TransformerExecutor => {
170
- return new TransformerExecutor(config);
171
- };
1
+ import { getEntityMetadata } from '../../orm/entity-metadata.js';
2
+ import type { EntityConstructor, EntityMetadata } from '../../orm/entity-metadata.js';
3
+ import type {
4
+ TransformContext,
5
+ TransformerConfig,
6
+ PropertyTransformer,
7
+ PropertyValidator,
8
+ PropertySanitizer,
9
+ TransformerMetadata
10
+ } from './transformer-metadata.js';
11
+
12
+ export class TransformerExecutor {
13
+ private config: TransformerConfig = {
14
+ auto: false,
15
+ execute: 'both',
16
+ stopOnFirstError: false
17
+ };
18
+
19
+ constructor(options?: TransformerConfig) {
20
+ this.config = { ...this.config, ...options };
21
+ }
22
+
23
+ /**
24
+ * Applies all transformers to an entity instance
25
+ */
26
+ async applyTransformers(entity: Record<string, unknown>, entityClass: EntityConstructor, context: Partial<TransformContext> = {}): Promise<void> {
27
+ const meta = getEntityMetadata(entityClass);
28
+ if (!meta || !meta.transformers) return;
29
+
30
+ // Get column types from entity metadata
31
+ const columnTypes = this.getColumnTypes(meta);
32
+
33
+ for (const [propertyName, transformerMeta] of Object.entries(meta.transformers)) {
34
+ const typedMeta = transformerMeta as unknown as TransformerMetadata;
35
+
36
+ // Skip if transformer should not execute in current context
37
+ if (!this.shouldExecute(typedMeta.executionOrder, context.isUpdate)) {
38
+ continue;
39
+ }
40
+
41
+ // Get current value
42
+ const value = entity[propertyName];
43
+
44
+ // Skip if value is null or undefined
45
+ if (value === null || value === undefined) continue;
46
+
47
+ // Create transform context
48
+ const transformContext: TransformContext = {
49
+ entityName: entityClass.name,
50
+ propertyName,
51
+ columnType: columnTypes[propertyName] || 'VARCHAR',
52
+ isUpdate: context.isUpdate || false,
53
+ originalValue: context.originalValue,
54
+ autoTransform: this.config.auto
55
+ };
56
+
57
+ try {
58
+ // Apply sanitizers first
59
+ let transformedValue = this.applySanitizers(value, typedMeta.sanitizers, transformContext);
60
+
61
+ // Apply transformers
62
+ transformedValue = this.applyTransformersToValue(transformedValue, typedMeta.transformers, transformContext);
63
+
64
+ // Apply validation
65
+ this.applyValidators(transformedValue, typedMeta.validators, transformContext);
66
+
67
+ // Update the entity with the transformed value
68
+ entity[propertyName] = transformedValue;
69
+ } catch (_error) {
70
+ // Handle errors based on config
71
+ if (this.config.stopOnFirstError) {
72
+ throw _error;
73
+ }
74
+ // Continue with other properties
75
+ }
76
+ }
77
+ }
78
+
79
+ private shouldExecute(executionOrder: 'before-save' | 'after-load' | 'both', isUpdate: boolean): boolean {
80
+ if (executionOrder === 'both') return true;
81
+ if (isUpdate && executionOrder === 'before-save') return true;
82
+ if (!isUpdate && executionOrder === 'after-load') return true;
83
+ return false;
84
+ }
85
+
86
+ private getColumnTypes(meta: EntityMetadata): Record<string, string> {
87
+ if (!meta || !meta.columns) return {};
88
+
89
+ const columnTypes: Record<string, string> = {};
90
+ for (const [propertyName, columnDef] of Object.entries(meta.columns)) {
91
+ columnTypes[propertyName] = columnDef.type || 'VARCHAR';
92
+ }
93
+ return columnTypes;
94
+ }
95
+
96
+ private applySanitizers(value: unknown, sanitizers: PropertySanitizer[], context: TransformContext): unknown {
97
+ let result = value;
98
+ for (const sanitizer of sanitizers) {
99
+ try {
100
+ result = sanitizer.sanitize(result, context);
101
+ } catch (_error: unknown) {
102
+ // Continue with next sanitizer if this one fails
103
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
104
+ const error = _error;
105
+ }
106
+ }
107
+ return result;
108
+ }
109
+
110
+ private applyTransformersToValue(value: unknown, transformers: PropertyTransformer[], context: TransformContext): unknown {
111
+ let result = value;
112
+ for (const transformer of transformers) {
113
+ try {
114
+ result = transformer.transform(result, context);
115
+ } catch (_error: unknown) {
116
+ // Continue with next transformer if this one fails
117
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
118
+ const error = _error;
119
+ }
120
+ }
121
+ return result;
122
+ }
123
+
124
+ private applyValidators(value: unknown, validators: PropertyValidator[], context: TransformContext): void {
125
+ const errors: string[] = [];
126
+
127
+ for (const validator of validators) {
128
+ try {
129
+ const result = validator.validate(value, context);
130
+
131
+ if (!result.isValid) {
132
+ if (context.autoTransform && 'autoTransform' in validator) {
133
+ const autoResult = (validator as { autoTransform?: (v: unknown, c: TransformContext) => { success: boolean; correctedValue?: unknown; message?: string } }).autoTransform?.(value, context);
134
+ if (typeof autoResult === 'object' && autoResult !== null && 'success' in autoResult && autoResult.success && 'correctedValue' in autoResult && autoResult.correctedValue !== undefined) {
135
+ // Auto-correct the value
136
+ (context as { correctedValue?: unknown }).correctedValue = autoResult.correctedValue;
137
+ return; // Value is now valid
138
+ }
139
+ }
140
+ errors.push(result.error || `Validation failed for ${validator.name}`);
141
+
142
+ if (this.config.stopOnFirstError) {
143
+ break;
144
+ }
145
+ }
146
+ } catch (_error: unknown) {
147
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
148
+ const error = _error;
149
+ if (_error instanceof Error) {
150
+ errors.push(_error.message);
151
+ } else {
152
+ errors.push('Validation failed');
153
+ }
154
+ if (this.config.stopOnFirstError) {
155
+ break;
156
+ }
157
+ }
158
+ }
159
+
160
+ if (errors.length > 0) {
161
+ throw new Error(errors.join(', '));
162
+ }
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Creates a transformer executor with specified config
168
+ */
169
+ export const createTransformerExecutor = (config?: TransformerConfig): TransformerExecutor => {
170
+ return new TransformerExecutor(config);
171
+ };