metal-orm 1.1.9 → 1.1.10

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 (73) hide show
  1. package/README.md +769 -764
  2. package/dist/index.cjs +2147 -239
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +559 -39
  5. package/dist/index.d.ts +559 -39
  6. package/dist/index.js +2119 -239
  7. package/dist/index.js.map +1 -1
  8. package/package.json +17 -12
  9. package/src/bulk/bulk-context.ts +83 -0
  10. package/src/bulk/bulk-delete-executor.ts +89 -0
  11. package/src/bulk/bulk-executor.base.ts +73 -0
  12. package/src/bulk/bulk-insert-executor.ts +74 -0
  13. package/src/bulk/bulk-types.ts +70 -0
  14. package/src/bulk/bulk-update-executor.ts +192 -0
  15. package/src/bulk/bulk-upsert-executor.ts +95 -0
  16. package/src/bulk/bulk-utils.ts +91 -0
  17. package/src/bulk/index.ts +18 -0
  18. package/src/codegen/typescript.ts +30 -21
  19. package/src/core/ast/expression-builders.ts +107 -10
  20. package/src/core/ast/expression-nodes.ts +52 -22
  21. package/src/core/ast/expression-visitor.ts +23 -13
  22. package/src/core/dialect/abstract.ts +30 -17
  23. package/src/core/dialect/mysql/index.ts +20 -5
  24. package/src/core/execution/db-executor.ts +96 -64
  25. package/src/core/execution/executors/better-sqlite3-executor.ts +94 -0
  26. package/src/core/execution/executors/mssql-executor.ts +66 -34
  27. package/src/core/execution/executors/mysql-executor.ts +98 -66
  28. package/src/core/execution/executors/postgres-executor.ts +33 -11
  29. package/src/core/execution/executors/sqlite-executor.ts +86 -30
  30. package/src/decorators/bootstrap.ts +482 -398
  31. package/src/decorators/column-decorator.ts +87 -96
  32. package/src/decorators/decorator-metadata.ts +100 -24
  33. package/src/decorators/entity.ts +27 -24
  34. package/src/decorators/relations.ts +231 -149
  35. package/src/decorators/transformers/transformer-decorators.ts +26 -29
  36. package/src/decorators/validators/country-validators-decorators.ts +9 -15
  37. package/src/dto/apply-filter.ts +568 -551
  38. package/src/index.ts +16 -9
  39. package/src/orm/entity-hydration.ts +116 -72
  40. package/src/orm/entity-metadata.ts +347 -301
  41. package/src/orm/entity-relations.ts +264 -207
  42. package/src/orm/entity.ts +199 -199
  43. package/src/orm/execute.ts +13 -13
  44. package/src/orm/lazy-batch/morph-many.ts +70 -0
  45. package/src/orm/lazy-batch/morph-one.ts +69 -0
  46. package/src/orm/lazy-batch/morph-to.ts +59 -0
  47. package/src/orm/lazy-batch.ts +4 -1
  48. package/src/orm/orm-session.ts +170 -104
  49. package/src/orm/pooled-executor-factory.ts +99 -58
  50. package/src/orm/query-logger.ts +49 -40
  51. package/src/orm/relation-change-processor.ts +198 -96
  52. package/src/orm/relations/belongs-to.ts +143 -143
  53. package/src/orm/relations/has-many.ts +204 -204
  54. package/src/orm/relations/has-one.ts +174 -174
  55. package/src/orm/relations/many-to-many.ts +288 -288
  56. package/src/orm/relations/morph-many.ts +156 -0
  57. package/src/orm/relations/morph-one.ts +151 -0
  58. package/src/orm/relations/morph-to.ts +162 -0
  59. package/src/orm/save-graph.ts +116 -1
  60. package/src/query-builder/expression-table-mapper.ts +5 -0
  61. package/src/query-builder/hydration-manager.ts +345 -345
  62. package/src/query-builder/hydration-planner.ts +178 -148
  63. package/src/query-builder/relation-conditions.ts +171 -151
  64. package/src/query-builder/relation-cte-builder.ts +5 -1
  65. package/src/query-builder/relation-filter-utils.ts +9 -6
  66. package/src/query-builder/relation-include-strategies.ts +44 -2
  67. package/src/query-builder/relation-join-strategies.ts +8 -1
  68. package/src/query-builder/relation-service.ts +250 -241
  69. package/src/query-builder/select/select-operations.ts +110 -105
  70. package/src/query-builder/update-include.ts +4 -0
  71. package/src/schema/relation.ts +296 -188
  72. package/src/schema/types.ts +138 -123
  73. package/src/tree/tree-decorator.ts +127 -137
@@ -1,96 +1,87 @@
1
- import { ColumnDef, ColumnType } from '../schema/column-types.js';
2
- import { ColumnDefLike } from '../orm/entity-metadata.js';
3
- import { getOrCreateMetadataBag } 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 normalizePropertyName = (name: string | symbol): string => {
51
- if (typeof name === 'symbol') {
52
- return name.description ?? name.toString();
53
- }
54
- return name;
55
- };
56
-
57
- const registerColumnFromContext = (
58
- context: ClassFieldDecoratorContext,
59
- column: ColumnDefLike
60
- ): void => {
61
- if (!context.name) {
62
- throw new Error('Column decorator requires a property name');
63
- }
64
- if (context.private) {
65
- throw new Error('Column decorator does not support private fields');
66
- }
67
- const propertyName = normalizePropertyName(context.name);
68
- const bag = getOrCreateMetadataBag(context);
69
- if (!bag.columns.some(entry => entry.propertyName === propertyName)) {
70
- bag.columns.push({ propertyName, column: { ...column } });
71
- }
72
- };
73
-
74
- /**
75
- * Decorator to define a column on an entity property.
76
- * @param definition - The column definition or options.
77
- * @returns A property decorator that registers the column metadata.
78
- */
79
- export function Column(definition: ColumnInput) {
80
- const normalized = normalizeColumnInput(definition);
81
- return function (_value: unknown, context: ClassFieldDecoratorContext) {
82
- registerColumnFromContext(context, normalized);
83
- };
84
- }
85
-
86
- /**
87
- * Decorator to define a primary key column on an entity property.
88
- * Sets the primary flag to true and delegates to Column decorator.
89
- * @param definition - The column definition or options.
90
- * @returns A property decorator that registers the primary key column metadata.
91
- */
92
- export function PrimaryKey(definition: ColumnInput) {
93
- const normalized = normalizeColumnInput(definition);
94
- normalized.primary = true;
95
- return Column(normalized);
96
- }
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,27 +1,32 @@
1
1
  import { ColumnDefLike, RelationMetadata } from '../orm/entity-metadata.js';
2
2
  import type { TransformerMetadata } from './transformers/transformer-metadata.js';
3
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
- }
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
18
 
19
19
  const METADATA_KEY = 'metal-orm:decorators';
20
+ const LEGACY_METADATA_KEY = Symbol.for('metal-orm:decorators:legacy');
20
21
 
21
22
  type MetadataCarrier = {
22
23
  metadata?: Record<PropertyKey, unknown>;
23
24
  };
24
25
 
26
+ type LegacyMetadataCarrier = {
27
+ constructor?: object;
28
+ };
29
+
25
30
  /**
26
31
  * Gets or creates a metadata bag for the given decorator context.
27
32
  * @param context - The decorator context with metadata support.
@@ -29,13 +34,23 @@ type MetadataCarrier = {
29
34
  */
30
35
  export const getOrCreateMetadataBag = (context: MetadataCarrier): DecoratorMetadataBag => {
31
36
  const metadata = context.metadata || (context.metadata = {} as Record<PropertyKey, unknown>);
32
- let bag = metadata[METADATA_KEY] as DecoratorMetadataBag | undefined;
33
- if (!bag) {
34
- bag = { columns: [], relations: [], transformers: [] };
35
- metadata[METADATA_KEY] = bag;
36
- }
37
- return bag;
38
- };
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
+ };
39
54
 
40
55
  /**
41
56
  * Reads the metadata bag from the given decorator context.
@@ -53,9 +68,14 @@ export const readMetadataBag = (context: MetadataCarrier): DecoratorMetadataBag
53
68
  */
54
69
  export const readMetadataBagFromConstructor = (ctor: object): DecoratorMetadataBag | undefined => {
55
70
  const metadataSymbol = (Symbol as { metadata?: symbol }).metadata;
56
- if (!metadataSymbol) return undefined;
57
- const metadata = Reflect.get(ctor, metadataSymbol) as Record<PropertyKey, unknown> | undefined;
58
- return metadata?.[METADATA_KEY] as DecoratorMetadataBag | undefined;
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;
59
79
  };
60
80
 
61
81
  /**
@@ -65,3 +85,59 @@ export const readMetadataBagFromConstructor = (ctor: object): DecoratorMetadataB
65
85
  */
66
86
  export const getDecoratorMetadata = (ctor: object): DecoratorMetadataBag | undefined =>
67
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,14 +1,14 @@
1
1
  import { TableHooks } from '../schema/table.js';
2
2
  import { RelationKinds } from '../schema/relation.js';
3
- import {
4
- addColumnMetadata,
5
- addRelationMetadata,
6
- EntityConstructor,
3
+ import {
4
+ addColumnMetadata,
5
+ addRelationMetadata,
6
+ EntityConstructor,
7
7
  ensureEntityMetadata,
8
- setEntityTableName
9
- } from '../orm/entity-metadata.js';
10
- import { readMetadataBag } from './decorator-metadata.js';
11
- import { syncTreeEntityMetadata } from '../tree/tree-decorator.js';
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
12
 
13
13
  /**
14
14
  * Options for defining an entity.
@@ -46,15 +46,18 @@ const deriveTableNameFromConstructor = (ctor: EntityConstructor<unknown>): strin
46
46
  * @returns A class decorator that registers the entity metadata.
47
47
  */
48
48
  export function Entity(options: EntityOptions = {}) {
49
- return function <T extends EntityConstructor>(value: T, context: ClassDecoratorContext): T {
49
+ return function <T extends EntityConstructor>(
50
+ value: T,
51
+ context?: ClassDecoratorContext
52
+ ): T {
50
53
  const ctor = value;
51
54
  const tableName = options.tableName ?? deriveTableNameFromConstructor(ctor);
52
55
  setEntityTableName(ctor, tableName, options.hooks, options.type);
53
56
 
54
- const bag = readMetadataBag(context);
55
- if (bag) {
56
- const meta = ensureEntityMetadata(ctor);
57
- for (const entry of bag.columns) {
57
+ const bag = context ? readMetadataBag(context) : readMetadataBagFromConstructor(ctor);
58
+ if (bag) {
59
+ const meta = ensureEntityMetadata(ctor);
60
+ for (const entry of bag.columns) {
58
61
  if (meta.columns[entry.propertyName]) {
59
62
  throw new Error(
60
63
  `Column '${entry.propertyName}' is already defined on entity '${ctor.name}'.`
@@ -77,14 +80,14 @@ export function Entity(options: EntityOptions = {}) {
77
80
  : undefined
78
81
  }
79
82
  : { ...entry.relation };
80
- addRelationMetadata(ctor, entry.propertyName, relationCopy);
81
- }
82
- }
83
-
84
- // Supports both decorator orders:
85
- // @Entity() above @Tree() and @Tree() above @Entity().
86
- syncTreeEntityMetadata(ctor, bag?.tree);
87
-
88
- return ctor;
89
- };
90
- }
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
+ }