metal-orm 1.1.11 → 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.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +100 -96
- package/scripts/generate-entities/schema.mjs +122 -122
- package/scripts/generate-entities/tree-detection.mjs +92 -92
- 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/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/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 +90 -90
- 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,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
|
+
};
|
package/src/decorators/entity.ts
CHANGED
|
@@ -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
|
+
}
|