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,87 +1,87 @@
1
- import { ColumnDef, ColumnType } from '../schema/column-types.js';
2
- import { ColumnDefLike } from '../orm/entity-metadata.js';
3
- import { resolveFieldDecoratorInfo } from './decorator-metadata.js';
4
-
5
- /**
6
- * Options for defining a column in an entity.
7
- */
8
- export interface ColumnOptions {
9
- type: ColumnType;
10
- args?: ColumnDef['args'];
11
- dialectTypes?: ColumnDef['dialectTypes'];
12
- notNull?: boolean;
13
- primary?: boolean;
14
- tsType?: ColumnDef['tsType'];
15
- name?: string;
16
- }
17
-
18
- /**
19
- * Input type for column definitions, either as options object or direct ColumnDef.
20
- */
21
- export type ColumnInput = ColumnOptions | ColumnDef;
22
-
23
- const normalizeColumnInput = (input: ColumnInput): ColumnDefLike => {
24
- const asOptions = input as ColumnOptions;
25
- const asDefinition = input as ColumnDef;
26
- const column: ColumnDefLike = {
27
- type: asOptions.type ?? asDefinition.type,
28
- args: asOptions.args ?? asDefinition.args,
29
- dialectTypes: asOptions.dialectTypes ?? asDefinition.dialectTypes,
30
- notNull: asOptions.notNull ?? asDefinition.notNull,
31
- primary: asOptions.primary ?? asDefinition.primary,
32
- tsType: asDefinition.tsType ?? asOptions.tsType,
33
- unique: asDefinition.unique,
34
- default: asDefinition.default,
35
- autoIncrement: asDefinition.autoIncrement,
36
- generated: asDefinition.generated,
37
- check: asDefinition.check,
38
- references: asDefinition.references,
39
- comment: asDefinition.comment,
40
- name: asOptions.name ?? asDefinition.name
41
- };
42
-
43
- if (!column.type) {
44
- throw new Error('Column decorator requires a column type');
45
- }
46
-
47
- return column;
48
- };
49
-
50
- const registerColumnFromContext = (
51
- targetOrValue: unknown,
52
- contextOrProperty: unknown,
53
- column: ColumnDefLike
54
- ): void => {
55
- const { propertyName, bag } = resolveFieldDecoratorInfo(
56
- targetOrValue,
57
- contextOrProperty,
58
- 'Column'
59
- );
60
- if (!bag.columns.some(entry => entry.propertyName === propertyName)) {
61
- bag.columns.push({ propertyName, column: { ...column } });
62
- }
63
- };
64
-
65
- /**
66
- * Decorator to define a column on an entity property.
67
- * @param definition - The column definition or options.
68
- * @returns A property decorator that registers the column metadata.
69
- */
70
- export function Column(definition: ColumnInput) {
71
- const normalized = normalizeColumnInput(definition);
72
- return function (targetOrValue: unknown, contextOrProperty: unknown) {
73
- registerColumnFromContext(targetOrValue, contextOrProperty, normalized);
74
- };
75
- }
76
-
77
- /**
78
- * Decorator to define a primary key column on an entity property.
79
- * Sets the primary flag to true and delegates to Column decorator.
80
- * @param definition - The column definition or options.
81
- * @returns A property decorator that registers the primary key column metadata.
82
- */
83
- export function PrimaryKey(definition: ColumnInput) {
84
- const normalized = normalizeColumnInput(definition);
85
- normalized.primary = true;
86
- return Column(normalized);
87
- }
1
+ import { ColumnDef, ColumnType } from '../schema/column-types.js';
2
+ import { ColumnDefLike } from '../orm/entity-metadata.js';
3
+ import { resolveFieldDecoratorInfo } from './decorator-metadata.js';
4
+
5
+ /**
6
+ * Options for defining a column in an entity.
7
+ */
8
+ export interface ColumnOptions {
9
+ type: ColumnType;
10
+ args?: ColumnDef['args'];
11
+ dialectTypes?: ColumnDef['dialectTypes'];
12
+ notNull?: boolean;
13
+ primary?: boolean;
14
+ tsType?: ColumnDef['tsType'];
15
+ name?: string;
16
+ }
17
+
18
+ /**
19
+ * Input type for column definitions, either as options object or direct ColumnDef.
20
+ */
21
+ export type ColumnInput = ColumnOptions | ColumnDef;
22
+
23
+ const normalizeColumnInput = (input: ColumnInput): ColumnDefLike => {
24
+ const asOptions = input as ColumnOptions;
25
+ const asDefinition = input as ColumnDef;
26
+ const column: ColumnDefLike = {
27
+ type: asOptions.type ?? asDefinition.type,
28
+ args: asOptions.args ?? asDefinition.args,
29
+ dialectTypes: asOptions.dialectTypes ?? asDefinition.dialectTypes,
30
+ notNull: asOptions.notNull ?? asDefinition.notNull,
31
+ primary: asOptions.primary ?? asDefinition.primary,
32
+ tsType: asDefinition.tsType ?? asOptions.tsType,
33
+ unique: asDefinition.unique,
34
+ default: asDefinition.default,
35
+ autoIncrement: asDefinition.autoIncrement,
36
+ generated: asDefinition.generated,
37
+ check: asDefinition.check,
38
+ references: asDefinition.references,
39
+ comment: asDefinition.comment,
40
+ name: asOptions.name ?? asDefinition.name
41
+ };
42
+
43
+ if (!column.type) {
44
+ throw new Error('Column decorator requires a column type');
45
+ }
46
+
47
+ return column;
48
+ };
49
+
50
+ const registerColumnFromContext = (
51
+ targetOrValue: unknown,
52
+ contextOrProperty: unknown,
53
+ column: ColumnDefLike
54
+ ): void => {
55
+ const { propertyName, bag } = resolveFieldDecoratorInfo(
56
+ targetOrValue,
57
+ contextOrProperty,
58
+ 'Column'
59
+ );
60
+ if (!bag.columns.some(entry => entry.propertyName === propertyName)) {
61
+ bag.columns.push({ propertyName, column: { ...column } });
62
+ }
63
+ };
64
+
65
+ /**
66
+ * Decorator to define a column on an entity property.
67
+ * @param definition - The column definition or options.
68
+ * @returns A property decorator that registers the column metadata.
69
+ */
70
+ export function Column(definition: ColumnInput) {
71
+ const normalized = normalizeColumnInput(definition);
72
+ return function (targetOrValue: unknown, contextOrProperty: unknown) {
73
+ registerColumnFromContext(targetOrValue, contextOrProperty, normalized);
74
+ };
75
+ }
76
+
77
+ /**
78
+ * Decorator to define a primary key column on an entity property.
79
+ * Sets the primary flag to true and delegates to Column decorator.
80
+ * @param definition - The column definition or options.
81
+ * @returns A property decorator that registers the primary key column metadata.
82
+ */
83
+ export function PrimaryKey(definition: ColumnInput) {
84
+ const normalized = normalizeColumnInput(definition);
85
+ normalized.primary = true;
86
+ return Column(normalized);
87
+ }
@@ -1,143 +1,143 @@
1
- import { ColumnDefLike, RelationMetadata } from '../orm/entity-metadata.js';
2
- import type { TransformerMetadata } from './transformers/transformer-metadata.js';
3
-
4
- /**
5
- * Bag for storing decorator metadata during the decoration phase.
6
- */
7
- export interface DecoratorTreeMetadata {
8
- parentProperty?: string;
9
- childrenProperty?: string;
10
- }
11
-
12
- export interface DecoratorMetadataBag {
13
- columns: Array<{ propertyName: string; column: ColumnDefLike }>;
14
- relations: Array<{ propertyName: string; relation: RelationMetadata }>;
15
- transformers: Array<{ propertyName: string; metadata: TransformerMetadata }>;
16
- tree?: DecoratorTreeMetadata;
17
- }
18
-
19
- const METADATA_KEY = 'metal-orm:decorators';
20
- const LEGACY_METADATA_KEY = Symbol.for('metal-orm:decorators:legacy');
21
-
22
- type MetadataCarrier = {
23
- metadata?: Record<PropertyKey, unknown>;
24
- };
25
-
26
- type LegacyMetadataCarrier = {
27
- constructor?: object;
28
- };
29
-
30
- /**
31
- * Gets or creates a metadata bag for the given decorator context.
32
- * @param context - The decorator context with metadata support.
33
- * @returns The metadata bag.
34
- */
35
- export const getOrCreateMetadataBag = (context: MetadataCarrier): DecoratorMetadataBag => {
36
- const metadata = context.metadata || (context.metadata = {} as Record<PropertyKey, unknown>);
37
- let bag = metadata[METADATA_KEY] as DecoratorMetadataBag | undefined;
38
- if (!bag) {
39
- bag = { columns: [], relations: [], transformers: [] };
40
- metadata[METADATA_KEY] = bag;
41
- }
42
- return bag;
43
- };
44
-
45
- const getOrCreateMetadataBagOnConstructor = (ctor: object): DecoratorMetadataBag => {
46
- const carrier = ctor as Record<PropertyKey, unknown>;
47
- let bag = carrier[LEGACY_METADATA_KEY] as DecoratorMetadataBag | undefined;
48
- if (!bag) {
49
- bag = { columns: [], relations: [], transformers: [] };
50
- carrier[LEGACY_METADATA_KEY] = bag;
51
- }
52
- return bag;
53
- };
54
-
55
- /**
56
- * Reads the metadata bag from the given decorator context.
57
- * @param context - The decorator context with metadata support.
58
- * @returns The metadata bag if present.
59
- */
60
- export const readMetadataBag = (context: MetadataCarrier): DecoratorMetadataBag | undefined => {
61
- return context.metadata?.[METADATA_KEY] as DecoratorMetadataBag | undefined;
62
- };
63
-
64
- /**
65
- * Reads the metadata bag from a decorated constructor when using standard decorators.
66
- * @param ctor - The entity constructor.
67
- * @returns The metadata bag if present.
68
- */
69
- export const readMetadataBagFromConstructor = (ctor: object): DecoratorMetadataBag | undefined => {
70
- const metadataSymbol = (Symbol as { metadata?: symbol }).metadata;
71
- if (metadataSymbol) {
72
- const metadata = Reflect.get(ctor, metadataSymbol) as Record<PropertyKey, unknown> | undefined;
73
- const stage3Bag = metadata?.[METADATA_KEY] as DecoratorMetadataBag | undefined;
74
- if (stage3Bag) {
75
- return stage3Bag;
76
- }
77
- }
78
- return (ctor as Record<PropertyKey, unknown>)[LEGACY_METADATA_KEY] as DecoratorMetadataBag | undefined;
79
- };
80
-
81
- /**
82
- * Public helper to read decorator metadata from a class constructor.
83
- * @param ctor - The entity constructor.
84
- * @returns The metadata bag if present.
85
- */
86
- export const getDecoratorMetadata = (ctor: object): DecoratorMetadataBag | undefined =>
87
- readMetadataBagFromConstructor(ctor);
88
-
89
- const normalizePropertyName = (name: string | symbol): string => {
90
- if (typeof name === 'symbol') {
91
- return name.description ?? name.toString();
92
- }
93
- return name;
94
- };
95
-
96
- const isStage3FieldContext = (value: unknown): value is ClassFieldDecoratorContext => {
97
- return (
98
- typeof value === 'object' &&
99
- value !== null &&
100
- 'name' in value &&
101
- 'private' in value &&
102
- 'metadata' in value
103
- );
104
- };
105
-
106
- export interface ResolvedFieldDecoratorInfo {
107
- bag: DecoratorMetadataBag;
108
- propertyName: string;
109
- }
110
-
111
- export const resolveFieldDecoratorInfo = (
112
- targetOrValue: unknown,
113
- contextOrProperty: unknown,
114
- decoratorName: string
115
- ): ResolvedFieldDecoratorInfo => {
116
- if (isStage3FieldContext(contextOrProperty)) {
117
- if (!contextOrProperty.name) {
118
- throw new Error(`${decoratorName} decorator requires a property name`);
119
- }
120
- if (contextOrProperty.private) {
121
- throw new Error(`${decoratorName} decorator does not support private fields`);
122
- }
123
- return {
124
- propertyName: normalizePropertyName(contextOrProperty.name),
125
- bag: getOrCreateMetadataBag(contextOrProperty)
126
- };
127
- }
128
-
129
- if (typeof contextOrProperty === 'string' || typeof contextOrProperty === 'symbol') {
130
- const legacyTarget = targetOrValue as LegacyMetadataCarrier | undefined;
131
- const ctor =
132
- typeof legacyTarget === 'function' ? legacyTarget : legacyTarget?.constructor;
133
- if (!ctor || (typeof ctor !== 'function' && typeof ctor !== 'object')) {
134
- throw new Error(`${decoratorName} decorator requires a class field target`);
135
- }
136
- return {
137
- propertyName: normalizePropertyName(contextOrProperty),
138
- bag: getOrCreateMetadataBagOnConstructor(ctor)
139
- };
140
- }
141
-
142
- throw new Error(`${decoratorName} decorator received an unsupported decorator context`);
143
- };
1
+ import { ColumnDefLike, RelationMetadata } from '../orm/entity-metadata.js';
2
+ import type { TransformerMetadata } from './transformers/transformer-metadata.js';
3
+
4
+ /**
5
+ * Bag for storing decorator metadata during the decoration phase.
6
+ */
7
+ export interface DecoratorTreeMetadata {
8
+ parentProperty?: string;
9
+ childrenProperty?: string;
10
+ }
11
+
12
+ export interface DecoratorMetadataBag {
13
+ columns: Array<{ propertyName: string; column: ColumnDefLike }>;
14
+ relations: Array<{ propertyName: string; relation: RelationMetadata }>;
15
+ transformers: Array<{ propertyName: string; metadata: TransformerMetadata }>;
16
+ tree?: DecoratorTreeMetadata;
17
+ }
18
+
19
+ const METADATA_KEY = 'metal-orm:decorators';
20
+ const LEGACY_METADATA_KEY = Symbol.for('metal-orm:decorators:legacy');
21
+
22
+ type MetadataCarrier = {
23
+ metadata?: Record<PropertyKey, unknown>;
24
+ };
25
+
26
+ type LegacyMetadataCarrier = {
27
+ constructor?: object;
28
+ };
29
+
30
+ /**
31
+ * Gets or creates a metadata bag for the given decorator context.
32
+ * @param context - The decorator context with metadata support.
33
+ * @returns The metadata bag.
34
+ */
35
+ export const getOrCreateMetadataBag = (context: MetadataCarrier): DecoratorMetadataBag => {
36
+ const metadata = context.metadata || (context.metadata = {} as Record<PropertyKey, unknown>);
37
+ let bag = metadata[METADATA_KEY] as DecoratorMetadataBag | undefined;
38
+ if (!bag) {
39
+ bag = { columns: [], relations: [], transformers: [] };
40
+ metadata[METADATA_KEY] = bag;
41
+ }
42
+ return bag;
43
+ };
44
+
45
+ const getOrCreateMetadataBagOnConstructor = (ctor: object): DecoratorMetadataBag => {
46
+ const carrier = ctor as Record<PropertyKey, unknown>;
47
+ let bag = carrier[LEGACY_METADATA_KEY] as DecoratorMetadataBag | undefined;
48
+ if (!bag) {
49
+ bag = { columns: [], relations: [], transformers: [] };
50
+ carrier[LEGACY_METADATA_KEY] = bag;
51
+ }
52
+ return bag;
53
+ };
54
+
55
+ /**
56
+ * Reads the metadata bag from the given decorator context.
57
+ * @param context - The decorator context with metadata support.
58
+ * @returns The metadata bag if present.
59
+ */
60
+ export const readMetadataBag = (context: MetadataCarrier): DecoratorMetadataBag | undefined => {
61
+ return context.metadata?.[METADATA_KEY] as DecoratorMetadataBag | undefined;
62
+ };
63
+
64
+ /**
65
+ * Reads the metadata bag from a decorated constructor when using standard decorators.
66
+ * @param ctor - The entity constructor.
67
+ * @returns The metadata bag if present.
68
+ */
69
+ export const readMetadataBagFromConstructor = (ctor: object): DecoratorMetadataBag | undefined => {
70
+ const metadataSymbol = (Symbol as { metadata?: symbol }).metadata;
71
+ if (metadataSymbol) {
72
+ const metadata = Reflect.get(ctor, metadataSymbol) as Record<PropertyKey, unknown> | undefined;
73
+ const stage3Bag = metadata?.[METADATA_KEY] as DecoratorMetadataBag | undefined;
74
+ if (stage3Bag) {
75
+ return stage3Bag;
76
+ }
77
+ }
78
+ return (ctor as Record<PropertyKey, unknown>)[LEGACY_METADATA_KEY] as DecoratorMetadataBag | undefined;
79
+ };
80
+
81
+ /**
82
+ * Public helper to read decorator metadata from a class constructor.
83
+ * @param ctor - The entity constructor.
84
+ * @returns The metadata bag if present.
85
+ */
86
+ export const getDecoratorMetadata = (ctor: object): DecoratorMetadataBag | undefined =>
87
+ readMetadataBagFromConstructor(ctor);
88
+
89
+ const normalizePropertyName = (name: string | symbol): string => {
90
+ if (typeof name === 'symbol') {
91
+ return name.description ?? name.toString();
92
+ }
93
+ return name;
94
+ };
95
+
96
+ const isStage3FieldContext = (value: unknown): value is ClassFieldDecoratorContext => {
97
+ return (
98
+ typeof value === 'object' &&
99
+ value !== null &&
100
+ 'name' in value &&
101
+ 'private' in value &&
102
+ 'metadata' in value
103
+ );
104
+ };
105
+
106
+ export interface ResolvedFieldDecoratorInfo {
107
+ bag: DecoratorMetadataBag;
108
+ propertyName: string;
109
+ }
110
+
111
+ export const resolveFieldDecoratorInfo = (
112
+ targetOrValue: unknown,
113
+ contextOrProperty: unknown,
114
+ decoratorName: string
115
+ ): ResolvedFieldDecoratorInfo => {
116
+ if (isStage3FieldContext(contextOrProperty)) {
117
+ if (!contextOrProperty.name) {
118
+ throw new Error(`${decoratorName} decorator requires a property name`);
119
+ }
120
+ if (contextOrProperty.private) {
121
+ throw new Error(`${decoratorName} decorator does not support private fields`);
122
+ }
123
+ return {
124
+ propertyName: normalizePropertyName(contextOrProperty.name),
125
+ bag: getOrCreateMetadataBag(contextOrProperty)
126
+ };
127
+ }
128
+
129
+ if (typeof contextOrProperty === 'string' || typeof contextOrProperty === 'symbol') {
130
+ const legacyTarget = targetOrValue as LegacyMetadataCarrier | undefined;
131
+ const ctor =
132
+ typeof legacyTarget === 'function' ? legacyTarget : legacyTarget?.constructor;
133
+ if (!ctor || (typeof ctor !== 'function' && typeof ctor !== 'object')) {
134
+ throw new Error(`${decoratorName} decorator requires a class field target`);
135
+ }
136
+ return {
137
+ propertyName: normalizePropertyName(contextOrProperty),
138
+ bag: getOrCreateMetadataBagOnConstructor(ctor)
139
+ };
140
+ }
141
+
142
+ throw new Error(`${decoratorName} decorator received an unsupported decorator context`);
143
+ };
@@ -1,93 +1,93 @@
1
- import { TableHooks } from '../schema/table.js';
2
- import { RelationKinds } from '../schema/relation.js';
3
- import {
4
- addColumnMetadata,
5
- addRelationMetadata,
6
- EntityConstructor,
7
- ensureEntityMetadata,
8
- setEntityTableName
9
- } from '../orm/entity-metadata.js';
10
- import { readMetadataBag, readMetadataBagFromConstructor } from './decorator-metadata.js';
11
- import { syncTreeEntityMetadata } from '../tree/tree-decorator.js';
12
-
13
- /**
14
- * Options for defining an entity.
15
- */
16
- export interface EntityOptions {
17
- tableName?: string;
18
- hooks?: TableHooks;
19
- /** Entity type: 'table' (default) or 'view'. Views are read-only. */
20
- type?: 'table' | 'view';
21
- }
22
-
23
- const toSnakeCase = (value: string): string => {
24
- return value
25
- .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
26
- .replace(/[^a-z0-9_]+/gi, '_')
27
- .replace(/__+/g, '_')
28
- .replace(/^_|_$/g, '')
29
- .toLowerCase();
30
- };
31
-
32
- const deriveTableNameFromConstructor = (ctor: EntityConstructor<unknown>): string => {
33
- const fallback = 'unknown';
34
- const rawName = ctor.name || fallback;
35
- const strippedName = rawName.replace(/Entity$/i, '');
36
- const normalized = toSnakeCase(strippedName || rawName);
37
- if (!normalized) {
38
- return fallback;
39
- }
40
- return normalized.endsWith('s') ? normalized : `${normalized}s`;
41
- };
42
-
43
- /**
44
- * Class decorator to mark a class as an entity and configure its table mapping.
45
- * @param options - Configuration options for the entity.
46
- * @returns A class decorator that registers the entity metadata.
47
- */
48
- export function Entity(options: EntityOptions = {}) {
49
- return function <T extends EntityConstructor>(
50
- value: T,
51
- context?: ClassDecoratorContext
52
- ): T {
53
- const ctor = value;
54
- const tableName = options.tableName ?? deriveTableNameFromConstructor(ctor);
55
- setEntityTableName(ctor, tableName, options.hooks, options.type);
56
-
57
- const bag = context ? readMetadataBag(context) : readMetadataBagFromConstructor(ctor);
58
- if (bag) {
59
- const meta = ensureEntityMetadata(ctor);
60
- for (const entry of bag.columns) {
61
- if (meta.columns[entry.propertyName]) {
62
- throw new Error(
63
- `Column '${entry.propertyName}' is already defined on entity '${ctor.name}'.`
64
- );
65
- }
66
- addColumnMetadata(ctor, entry.propertyName, { ...entry.column });
67
- }
68
- for (const entry of bag.relations) {
69
- if (meta.relations[entry.propertyName]) {
70
- throw new Error(
71
- `Relation '${entry.propertyName}' is already defined on entity '${ctor.name}'.`
72
- );
73
- }
74
- const relationCopy =
75
- entry.relation.kind === RelationKinds.BelongsToMany
76
- ? {
77
- ...entry.relation,
78
- defaultPivotColumns: entry.relation.defaultPivotColumns
79
- ? [...entry.relation.defaultPivotColumns]
80
- : undefined
81
- }
82
- : { ...entry.relation };
83
- addRelationMetadata(ctor, entry.propertyName, relationCopy);
84
- }
85
- }
86
-
87
- // Supports both decorator orders:
88
- // @Entity() above @Tree() and @Tree() above @Entity().
89
- syncTreeEntityMetadata(ctor, bag?.tree);
90
-
91
- return ctor;
92
- };
93
- }
1
+ import { TableHooks } from '../schema/table.js';
2
+ import { RelationKinds } from '../schema/relation.js';
3
+ import {
4
+ addColumnMetadata,
5
+ addRelationMetadata,
6
+ EntityConstructor,
7
+ ensureEntityMetadata,
8
+ setEntityTableName
9
+ } from '../orm/entity-metadata.js';
10
+ import { readMetadataBag, readMetadataBagFromConstructor } from './decorator-metadata.js';
11
+ import { syncTreeEntityMetadata } from '../tree/tree-decorator.js';
12
+
13
+ /**
14
+ * Options for defining an entity.
15
+ */
16
+ export interface EntityOptions {
17
+ tableName?: string;
18
+ hooks?: TableHooks;
19
+ /** Entity type: 'table' (default) or 'view'. Views are read-only. */
20
+ type?: 'table' | 'view';
21
+ }
22
+
23
+ const toSnakeCase = (value: string): string => {
24
+ return value
25
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
26
+ .replace(/[^a-z0-9_]+/gi, '_')
27
+ .replace(/__+/g, '_')
28
+ .replace(/^_|_$/g, '')
29
+ .toLowerCase();
30
+ };
31
+
32
+ const deriveTableNameFromConstructor = (ctor: EntityConstructor<unknown>): string => {
33
+ const fallback = 'unknown';
34
+ const rawName = ctor.name || fallback;
35
+ const strippedName = rawName.replace(/Entity$/i, '');
36
+ const normalized = toSnakeCase(strippedName || rawName);
37
+ if (!normalized) {
38
+ return fallback;
39
+ }
40
+ return normalized.endsWith('s') ? normalized : `${normalized}s`;
41
+ };
42
+
43
+ /**
44
+ * Class decorator to mark a class as an entity and configure its table mapping.
45
+ * @param options - Configuration options for the entity.
46
+ * @returns A class decorator that registers the entity metadata.
47
+ */
48
+ export function Entity(options: EntityOptions = {}) {
49
+ return function <T extends EntityConstructor>(
50
+ value: T,
51
+ context?: ClassDecoratorContext
52
+ ): T {
53
+ const ctor = value;
54
+ const tableName = options.tableName ?? deriveTableNameFromConstructor(ctor);
55
+ setEntityTableName(ctor, tableName, options.hooks, options.type);
56
+
57
+ const bag = context ? readMetadataBag(context) : readMetadataBagFromConstructor(ctor);
58
+ if (bag) {
59
+ const meta = ensureEntityMetadata(ctor);
60
+ for (const entry of bag.columns) {
61
+ if (meta.columns[entry.propertyName]) {
62
+ throw new Error(
63
+ `Column '${entry.propertyName}' is already defined on entity '${ctor.name}'.`
64
+ );
65
+ }
66
+ addColumnMetadata(ctor, entry.propertyName, { ...entry.column });
67
+ }
68
+ for (const entry of bag.relations) {
69
+ if (meta.relations[entry.propertyName]) {
70
+ throw new Error(
71
+ `Relation '${entry.propertyName}' is already defined on entity '${ctor.name}'.`
72
+ );
73
+ }
74
+ const relationCopy =
75
+ entry.relation.kind === RelationKinds.BelongsToMany
76
+ ? {
77
+ ...entry.relation,
78
+ defaultPivotColumns: entry.relation.defaultPivotColumns
79
+ ? [...entry.relation.defaultPivotColumns]
80
+ : undefined
81
+ }
82
+ : { ...entry.relation };
83
+ addRelationMetadata(ctor, entry.propertyName, relationCopy);
84
+ }
85
+ }
86
+
87
+ // Supports both decorator orders:
88
+ // @Entity() above @Tree() and @Tree() above @Entity().
89
+ syncTreeEntityMetadata(ctor, bag?.tree);
90
+
91
+ return ctor;
92
+ };
93
+ }