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.
- package/README.md +758 -769
- package/dist/index.cjs +106 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +106 -43
- package/dist/index.js.map +1 -1
- package/package.json +100 -96
- package/scripts/generate-entities/render.mjs +21 -12
- package/scripts/generate-entities/schema.mjs +209 -195
- package/scripts/generate-entities/tree-detection.mjs +159 -153
- package/scripts/inflection/pt-br.mjs +468 -468
- package/scripts/run-eslint.mjs +33 -34
- package/scripts/show-sql-seed.sql +102 -0
- package/scripts/show-sql.mjs +321 -325
- package/src/bulk/bulk-delete-executor.ts +3 -5
- package/src/bulk/bulk-insert-executor.ts +7 -7
- package/src/bulk/bulk-update-executor.ts +2 -2
- package/src/bulk/bulk-upsert-executor.ts +5 -7
- package/src/codegen/naming-strategy.ts +54 -54
- package/src/codegen/typescript.ts +482 -482
- package/src/core/ast/adapters.ts +1 -1
- package/src/core/ast/builders.ts +123 -123
- package/src/core/ast/expression-builders.ts +745 -745
- package/src/core/ast/expression-nodes.ts +304 -304
- package/src/core/ast/expression-visitor.ts +212 -212
- package/src/core/ast/expression.ts +20 -20
- package/src/core/ast/helpers.ts +14 -14
- package/src/core/ast/join.ts +18 -18
- package/src/core/ast/query.ts +200 -200
- package/src/core/ddl/dialects/base-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/render-reference.test.ts +70 -70
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -1
- package/src/core/ddl/introspect/catalogs/index.ts +5 -5
- package/src/core/ddl/introspect/catalogs/postgres.ts +146 -146
- package/src/core/ddl/introspect/context.ts +15 -15
- package/src/core/ddl/introspect/functions/postgres.ts +35 -35
- package/src/core/ddl/introspect/mysql.ts +113 -36
- package/src/core/ddl/introspect/registry.ts +40 -40
- package/src/core/ddl/introspect/run-select.ts +35 -35
- package/src/core/ddl/introspect/utils.ts +56 -56
- package/src/core/ddl/naming-strategy.ts +16 -16
- package/src/core/ddl/schema-dialect.ts +56 -56
- package/src/core/ddl/schema-diff.ts +234 -234
- package/src/core/ddl/schema-generator.ts +1 -1
- package/src/core/ddl/schema-introspect.ts +25 -25
- package/src/core/ddl/sql-writing.ts +171 -171
- package/src/core/dialect/abstract.ts +541 -541
- package/src/core/dialect/base/cte-compiler.ts +33 -33
- package/src/core/dialect/base/function-table-formatter.ts +103 -103
- package/src/core/dialect/base/groupby-compiler.ts +21 -21
- package/src/core/dialect/base/join-compiler.ts +25 -25
- package/src/core/dialect/base/orderby-compiler.ts +35 -35
- package/src/core/dialect/base/pagination-strategy.ts +32 -32
- package/src/core/dialect/base/returning-strategy.ts +57 -57
- package/src/core/dialect/base/sql-dialect.ts +294 -294
- package/src/core/dialect/dialect-factory.ts +91 -91
- package/src/core/dialect/mssql/functions.ts +120 -120
- package/src/core/dialect/mssql/index.ts +317 -317
- package/src/core/dialect/mysql/functions.ts +103 -103
- package/src/core/dialect/mysql/index.ts +105 -105
- package/src/core/dialect/postgres/functions.ts +108 -108
- package/src/core/dialect/postgres/index.ts +91 -91
- package/src/core/dialect/sqlite/functions.ts +129 -129
- package/src/core/dialect/sqlite/index.ts +67 -67
- package/src/core/driver/database-driver.ts +19 -19
- package/src/core/driver/mssql-driver.ts +23 -23
- package/src/core/driver/mysql-driver.ts +23 -23
- package/src/core/driver/postgres-driver.ts +23 -23
- package/src/core/driver/sqlite-driver.ts +23 -23
- package/src/core/execution/db-executor.ts +92 -92
- package/src/core/execution/executors/better-sqlite3-executor.ts +94 -94
- package/src/core/execution/executors/mssql-executor.ts +184 -184
- package/src/core/execution/executors/mysql-executor.ts +88 -88
- package/src/core/execution/executors/postgres-executor.ts +26 -26
- package/src/core/execution/executors/sqlite-executor.ts +42 -42
- package/src/core/execution/pooling/pool-types.ts +30 -30
- package/src/core/execution/pooling/pool.ts +275 -275
- package/src/core/functions/array.ts +35 -35
- package/src/core/functions/control-flow.ts +79 -79
- package/src/core/functions/datetime.ts +237 -237
- package/src/core/functions/json.ts +70 -70
- package/src/core/functions/numeric.ts +297 -297
- package/src/core/functions/text.ts +334 -334
- package/src/core/functions/types.ts +33 -33
- package/src/core/hydration/types.ts +51 -51
- package/src/core/sql/sql-operator-config.ts +39 -39
- package/src/core/sql/sql.ts +152 -152
- package/src/decorators/column-decorator.ts +87 -87
- package/src/decorators/decorator-metadata.ts +143 -143
- package/src/decorators/entity.ts +93 -93
- package/src/decorators/index.ts +39 -39
- package/src/decorators/relations.ts +231 -231
- package/src/decorators/transformers/built-in/string-transformers.ts +231 -231
- package/src/decorators/transformers/transformer-decorators.ts +134 -134
- package/src/decorators/transformers/transformer-executor.ts +171 -171
- package/src/decorators/transformers/transformer-metadata.ts +73 -73
- package/src/decorators/validators/built-in/br-cep-validator.ts +55 -55
- package/src/decorators/validators/built-in/br-cnpj-validator.ts +105 -105
- package/src/decorators/validators/built-in/br-cpf-validator.ts +103 -103
- package/src/decorators/validators/country-validator-registry.ts +64 -64
- package/src/decorators/validators/country-validators-decorators.ts +203 -203
- package/src/decorators/validators/country-validators.ts +105 -105
- package/src/decorators/validators/validator-metadata.ts +59 -59
- package/src/dto/dto-types.ts +226 -226
- package/src/dto/filter-types.ts +141 -141
- package/src/dto/index.ts +95 -95
- package/src/dto/openapi/generators/base.ts +29 -29
- package/src/dto/openapi/generators/column.ts +37 -37
- package/src/dto/openapi/generators/dto.ts +91 -91
- package/src/dto/openapi/generators/filter.ts +75 -75
- package/src/dto/openapi/generators/nested-dto.ts +618 -618
- package/src/dto/openapi/generators/pagination.ts +111 -111
- package/src/dto/openapi/generators/relation-filter.ts +228 -228
- package/src/dto/openapi/index.ts +12 -12
- package/src/dto/openapi/types.ts +101 -101
- package/src/dto/openapi/utilities.ts +95 -95
- package/src/dto/pagination-utils.ts +150 -150
- package/src/dto/transform.ts +197 -197
- package/src/index.ts +86 -86
- package/src/orm/als.ts +58 -58
- package/src/orm/domain-event-bus.ts +119 -119
- package/src/orm/entity-context.ts +86 -86
- package/src/orm/entity-materializer.ts +159 -159
- package/src/orm/entity-meta.ts +97 -97
- package/src/orm/entity-registry.ts +39 -39
- package/src/orm/entity-relation-cache.ts +39 -39
- package/src/orm/execute.ts +194 -194
- package/src/orm/execution-context.ts +18 -18
- package/src/orm/hydration-context.ts +26 -26
- package/src/orm/hydration.ts +112 -112
- package/src/orm/identity-map.ts +61 -61
- package/src/orm/interceptor-pipeline.ts +16 -16
- package/src/orm/lazy-batch/belongs-to-many.ts +119 -119
- package/src/orm/lazy-batch/belongs-to.ts +108 -108
- package/src/orm/lazy-batch/has-many.ts +69 -69
- package/src/orm/lazy-batch/has-one.ts +68 -68
- package/src/orm/lazy-batch/shared.ts +125 -125
- package/src/orm/orm-session.ts +620 -620
- package/src/orm/orm.ts +34 -34
- package/src/orm/pooled-executor-factory.ts +121 -121
- package/src/orm/query-logger.ts +40 -40
- package/src/orm/relation-change-processor.ts +420 -420
- package/src/orm/relations/belongs-to.ts +143 -143
- package/src/orm/relations/has-many.ts +204 -204
- package/src/orm/relations/has-one.ts +174 -174
- package/src/orm/relations/many-to-many.ts +288 -288
- package/src/orm/relations/morph-many.ts +156 -156
- package/src/orm/relations/morph-one.ts +151 -151
- package/src/orm/relations/morph-to.ts +162 -162
- package/src/orm/runtime-types.ts +92 -92
- package/src/orm/save-graph.ts +561 -561
- package/src/orm/transaction-runner.ts +24 -24
- package/src/orm/unit-of-work.ts +426 -426
- package/src/query/index.ts +69 -69
- package/src/query/target.ts +46 -46
- package/src/query-builder/column-selector.ts +81 -81
- package/src/query-builder/delete.ts +141 -141
- package/src/query-builder/insert-query-state.ts +149 -149
- package/src/query-builder/insert.ts +117 -117
- package/src/query-builder/query-ast-service.ts +287 -287
- package/src/query-builder/query-resolution.ts +78 -78
- package/src/query-builder/raw-column-parser.ts +38 -38
- package/src/query-builder/relation-filter-utils.ts +153 -153
- package/src/query-builder/relation-join-planner.ts +10 -10
- package/src/query-builder/relation-manager.ts +87 -87
- package/src/query-builder/relation-projection-helper.ts +102 -102
- package/src/query-builder/relation-types.ts +9 -9
- package/src/query-builder/relation-utils.ts +15 -15
- package/src/query-builder/select/cte-facet.ts +40 -40
- package/src/query-builder/select/from-facet.ts +80 -80
- package/src/query-builder/select/join-facet.ts +62 -62
- package/src/query-builder/select/predicate-facet.ts +104 -104
- package/src/query-builder/select/projection-facet.ts +70 -70
- package/src/query-builder/select/relation-facet.ts +81 -81
- package/src/query-builder/select/setop-facet.ts +36 -36
- package/src/query-builder/select-helpers.ts +1 -1
- package/src/query-builder/select-query-builder-deps.ts +130 -130
- package/src/query-builder/select-query-state.ts +207 -207
- package/src/query-builder/update.ts +151 -151
- package/src/schema/column-types.ts +322 -322
- package/src/schema/table-guards.ts +37 -37
- package/src/schema/table.ts +281 -281
- package/src/tree/index.ts +13 -13
- package/src/tree/nested-set-strategy.ts +545 -545
- package/src/tree/tree-decorator.ts +397 -397
- package/src/tree/tree-manager.ts +754 -754
- package/src/tree/tree-query.ts +427 -427
- 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
|
+
};
|