metal-orm 1.0.57 → 1.0.59

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 (46) hide show
  1. package/README.md +23 -13
  2. package/dist/index.cjs +1750 -733
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +244 -157
  5. package/dist/index.d.ts +244 -157
  6. package/dist/index.js +1745 -733
  7. package/dist/index.js.map +1 -1
  8. package/package.json +69 -69
  9. package/src/core/ddl/schema-generator.ts +44 -1
  10. package/src/decorators/bootstrap.ts +186 -113
  11. package/src/decorators/column-decorator.ts +8 -49
  12. package/src/decorators/decorator-metadata.ts +10 -46
  13. package/src/decorators/entity.ts +30 -40
  14. package/src/decorators/relations.ts +30 -56
  15. package/src/orm/entity-hydration.ts +72 -0
  16. package/src/orm/entity-meta.ts +18 -13
  17. package/src/orm/entity-metadata.ts +240 -238
  18. package/src/orm/entity-relation-cache.ts +39 -0
  19. package/src/orm/entity-relations.ts +207 -0
  20. package/src/orm/entity.ts +124 -343
  21. package/src/orm/execute.ts +87 -20
  22. package/src/orm/lazy-batch/belongs-to-many.ts +134 -0
  23. package/src/orm/lazy-batch/belongs-to.ts +108 -0
  24. package/src/orm/lazy-batch/has-many.ts +69 -0
  25. package/src/orm/lazy-batch/has-one.ts +68 -0
  26. package/src/orm/lazy-batch/shared.ts +125 -0
  27. package/src/orm/lazy-batch.ts +4 -309
  28. package/src/orm/relations/belongs-to.ts +2 -2
  29. package/src/orm/relations/has-many.ts +23 -9
  30. package/src/orm/relations/has-one.ts +2 -2
  31. package/src/orm/relations/many-to-many.ts +29 -14
  32. package/src/orm/save-graph-types.ts +2 -2
  33. package/src/orm/save-graph.ts +18 -18
  34. package/src/query-builder/relation-conditions.ts +80 -59
  35. package/src/query-builder/relation-cte-builder.ts +63 -0
  36. package/src/query-builder/relation-filter-utils.ts +159 -0
  37. package/src/query-builder/relation-include-strategies.ts +177 -0
  38. package/src/query-builder/relation-join-planner.ts +80 -0
  39. package/src/query-builder/relation-service.ts +103 -159
  40. package/src/query-builder/relation-types.ts +43 -12
  41. package/src/query-builder/select/projection-facet.ts +23 -23
  42. package/src/query-builder/select/select-operations.ts +145 -0
  43. package/src/query-builder/select.ts +373 -426
  44. package/src/schema/relation.ts +22 -18
  45. package/src/schema/table.ts +22 -9
  46. package/src/schema/types.ts +103 -84
@@ -57,12 +57,15 @@ export interface BelongsToRelation<TTarget extends TableDef = TableDef> {
57
57
  /**
58
58
  * Many-to-many relationship definition with rich pivot metadata
59
59
  */
60
- export interface BelongsToManyRelation<TTarget extends TableDef = TableDef> {
61
- type: typeof RelationKinds.BelongsToMany;
62
- target: TTarget;
63
- pivotTable: TableDef;
64
- pivotForeignKeyToRoot: string;
65
- pivotForeignKeyToTarget: string;
60
+ export interface BelongsToManyRelation<
61
+ TTarget extends TableDef = TableDef,
62
+ TPivot extends TableDef = TableDef
63
+ > {
64
+ type: typeof RelationKinds.BelongsToMany;
65
+ target: TTarget;
66
+ pivotTable: TPivot;
67
+ pivotForeignKeyToRoot: string;
68
+ pivotForeignKeyToTarget: string;
66
69
  localKey?: string;
67
70
  targetKey?: string;
68
71
  pivotPrimaryKey?: string;
@@ -156,24 +159,25 @@ export const belongsTo = <TTarget extends TableDef>(
156
159
  * @param options - Pivot metadata configuration
157
160
  * @returns BelongsToManyRelation definition
158
161
  */
159
- export const belongsToMany = <
160
- TTarget extends TableDef
161
- >(
162
- target: TTarget,
163
- pivotTable: TableDef,
164
- options: {
165
- pivotForeignKeyToRoot: string;
166
- pivotForeignKeyToTarget: string;
162
+ export const belongsToMany = <
163
+ TTarget extends TableDef,
164
+ TPivot extends TableDef = TableDef
165
+ >(
166
+ target: TTarget,
167
+ pivotTable: TPivot,
168
+ options: {
169
+ pivotForeignKeyToRoot: string;
170
+ pivotForeignKeyToTarget: string;
167
171
  localKey?: string;
168
172
  targetKey?: string;
169
173
  pivotPrimaryKey?: string;
170
174
  defaultPivotColumns?: string[];
171
175
  cascade?: CascadeMode;
172
176
  }
173
- ): BelongsToManyRelation<TTarget> => ({
174
- type: RelationKinds.BelongsToMany,
175
- target,
176
- pivotTable,
177
+ ): BelongsToManyRelation<TTarget, TPivot> => ({
178
+ type: RelationKinds.BelongsToMany,
179
+ target,
180
+ pivotTable,
177
181
  pivotForeignKeyToRoot: options.pivotForeignKeyToRoot,
178
182
  pivotForeignKeyToTarget: options.pivotForeignKeyToTarget,
179
183
  localKey: options.localKey,
@@ -85,13 +85,13 @@ export interface TableDef<T extends Record<string, ColumnDef> = Record<string, C
85
85
  * });
86
86
  * ```
87
87
  */
88
- export const defineTable = <T extends Record<string, ColumnDef>>(
89
- name: string,
90
- columns: T,
91
- relations: Record<string, RelationDef> = {},
92
- hooks?: TableHooks,
93
- options: TableOptions = {}
94
- ): TableDef<T> => {
88
+ export const defineTable = <T extends Record<string, ColumnDef>>(
89
+ name: string,
90
+ columns: T,
91
+ relations: Record<string, RelationDef> = {},
92
+ hooks?: TableHooks,
93
+ options: TableOptions = {}
94
+ ): TableDef<T> => {
95
95
  // Runtime mutability to assign names to column definitions for convenience
96
96
  const colsWithNames = Object.entries(columns).reduce((acc, [key, def]) => {
97
97
  const colDef = { ...def, name: key, table: name };
@@ -112,8 +112,21 @@ export const defineTable = <T extends Record<string, ColumnDef>>(
112
112
  engine: options.engine,
113
113
  charset: options.charset,
114
114
  collation: options.collation
115
- };
116
- };
115
+ };
116
+ };
117
+
118
+ /**
119
+ * Assigns relations to a table definition while preserving literal typing.
120
+ */
121
+ export function setRelations<
122
+ TTable extends TableDef,
123
+ TRelations extends Record<string, RelationDef>
124
+ >(
125
+ table: TTable,
126
+ relations: TRelations
127
+ ): asserts table is TTable & { relations: TRelations } {
128
+ table.relations = relations;
129
+ }
117
130
 
118
131
  type DirectColumnKeys<T extends TableDef> =
119
132
  Exclude<keyof T["columns"] & string, keyof T | "$">;
@@ -1,111 +1,130 @@
1
- import type { ColumnDef } from './column-types.js';
2
- export type { ColumnDef };
3
- import { TableDef } from './table.js';
4
- import {
5
- RelationDef,
6
- HasManyRelation,
7
- HasOneRelation,
8
- BelongsToRelation,
9
- BelongsToManyRelation
10
- } from './relation.js';
11
-
12
- /**
13
- * Resolves a relation definition to its target table type.
14
- */
1
+ import type { ColumnDef } from './column-types.js';
2
+ export type { ColumnDef };
3
+ import { TableDef } from './table.js';
4
+ import {
5
+ RelationDef,
6
+ HasManyRelation,
7
+ HasOneRelation,
8
+ BelongsToRelation,
9
+ BelongsToManyRelation
10
+ } from './relation.js';
11
+
12
+ /**
13
+ * Resolves a relation definition to its target table type.
14
+ */
15
15
  export type RelationTargetTable<TRel extends RelationDef> =
16
16
  TRel extends HasManyRelation<infer TTarget> ? TTarget :
17
17
  TRel extends HasOneRelation<infer TTarget> ? TTarget :
18
18
  TRel extends BelongsToRelation<infer TTarget> ? TTarget :
19
- TRel extends BelongsToManyRelation<infer TTarget> ? TTarget :
19
+ TRel extends BelongsToManyRelation<infer TTarget, TableDef> ? TTarget :
20
20
  never;
21
-
22
- type NormalizedColumnType<T extends ColumnDef> = Lowercase<T['type'] & string>;
23
-
24
- /**
25
- * Maps a ColumnDef to its TypeScript type representation
26
- */
27
- export type ColumnToTs<T extends ColumnDef> =
28
- [unknown] extends [T['tsType']]
29
- ? NormalizedColumnType<T> extends 'int' | 'integer' ? number :
30
- NormalizedColumnType<T> extends 'bigint' ? number | bigint :
31
- NormalizedColumnType<T> extends 'decimal' | 'float' | 'double' ? number :
32
- NormalizedColumnType<T> extends 'boolean' ? boolean :
33
- NormalizedColumnType<T> extends 'json' ? unknown :
34
- NormalizedColumnType<T> extends 'blob' | 'binary' | 'varbinary' | 'bytea' ? Buffer :
35
- NormalizedColumnType<T> extends 'date' | 'datetime' | 'timestamp' | 'timestamptz' ? string :
36
- string
37
- : Exclude<T['tsType'], undefined>;
38
-
39
- /**
40
- * Infers a row shape from a table definition
41
- */
42
- export type InferRow<TTable extends TableDef> = {
43
- [K in keyof TTable['columns']]: ColumnToTs<TTable['columns'][K]>;
44
- };
45
-
21
+
22
+ type NormalizedColumnType<T extends ColumnDef> = Lowercase<T['type'] & string>;
23
+
24
+ /**
25
+ * Maps a ColumnDef to its TypeScript type representation
26
+ */
27
+ export type ColumnToTs<T extends ColumnDef> =
28
+ [unknown] extends [T['tsType']]
29
+ ? NormalizedColumnType<T> extends 'int' | 'integer' ? number :
30
+ NormalizedColumnType<T> extends 'bigint' ? number | bigint :
31
+ NormalizedColumnType<T> extends 'decimal' | 'float' | 'double' ? number :
32
+ NormalizedColumnType<T> extends 'boolean' ? boolean :
33
+ NormalizedColumnType<T> extends 'json' ? unknown :
34
+ NormalizedColumnType<T> extends 'blob' | 'binary' | 'varbinary' | 'bytea' ? Buffer :
35
+ NormalizedColumnType<T> extends 'date' | 'datetime' | 'timestamp' | 'timestamptz' ? string :
36
+ string
37
+ : Exclude<T['tsType'], undefined>;
38
+
39
+ /**
40
+ * Infers a row shape from a table definition
41
+ */
42
+ export type InferRow<TTable extends TableDef> = {
43
+ [K in keyof TTable['columns']]: ColumnToTs<TTable['columns'][K]>;
44
+ };
45
+
46
46
  type RelationResult<T extends RelationDef> =
47
47
  T extends HasManyRelation<infer TTarget> ? InferRow<TTarget>[] :
48
48
  T extends HasOneRelation<infer TTarget> ? InferRow<TTarget> | null :
49
49
  T extends BelongsToRelation<infer TTarget> ? InferRow<TTarget> | null :
50
- T extends BelongsToManyRelation<infer TTarget> ? (InferRow<TTarget> & { _pivot?: unknown })[] :
50
+ T extends BelongsToManyRelation<infer TTarget, TableDef> ? (InferRow<TTarget> & { _pivot?: Record<string, unknown> })[] :
51
51
  never;
52
-
53
- /**
54
- * Maps relation names to the expected row results
55
- */
56
- export type RelationMap<TTable extends TableDef> = {
57
- [K in keyof TTable['relations']]: RelationResult<TTable['relations'][K]>;
58
- };
52
+
53
+ /**
54
+ * Maps relation names to the expected row results
55
+ */
56
+ export type RelationMap<TTable extends TableDef> = {
57
+ [K in keyof TTable['relations']]: RelationResult<TTable['relations'][K]>;
58
+ };
59
+
60
+ type RelationWrapper<TRel extends RelationDef> =
61
+ TRel extends HasManyRelation<infer TTarget>
62
+ ? HasManyCollection<EntityInstance<TTarget>> & ReadonlyArray<EntityInstance<TTarget>>
63
+ : TRel extends HasOneRelation<infer TTarget>
64
+ ? HasOneReference<EntityInstance<TTarget>>
65
+ : TRel extends BelongsToManyRelation<infer TTarget>
66
+ ? ManyToManyCollection<EntityInstance<TTarget> & { _pivot?: Record<string, unknown> }>
67
+ & ReadonlyArray<EntityInstance<TTarget> & { _pivot?: Record<string, unknown> }>
68
+ : TRel extends BelongsToRelation<infer TTarget>
69
+ ? BelongsToReference<EntityInstance<TTarget>>
70
+ : never;
59
71
 
60
72
  export interface HasManyCollection<TChild> {
73
+ length: number;
74
+ [Symbol.iterator](): Iterator<TChild>;
61
75
  load(): Promise<TChild[]>;
62
76
  getItems(): TChild[];
63
77
  add(data: Partial<TChild>): TChild;
64
78
  attach(entity: TChild): void;
65
79
  remove(entity: TChild): void;
66
- clear(): void;
67
- }
68
-
69
- export interface BelongsToReference<TParent> {
70
- load(): Promise<TParent | null>;
71
- get(): TParent | null;
72
- set(data: Partial<TParent> | TParent | null): TParent | null;
73
- }
74
-
75
- export interface HasOneReference<TChild> {
76
- load(): Promise<TChild | null>;
77
- get(): TChild | null;
78
- set(data: Partial<TChild> | TChild | null): TChild | null;
79
- }
80
-
81
- export interface ManyToManyCollection<TTarget> {
80
+ clear(): void;
81
+ }
82
+
83
+ export interface BelongsToReferenceApi<TParent extends object = object> {
84
+ load(): Promise<TParent | null>;
85
+ get(): TParent | null;
86
+ set(data: Partial<TParent> | TParent | null): TParent | null;
87
+ }
88
+
89
+ export type BelongsToReference<TParent extends object = object> = BelongsToReferenceApi<TParent> & Partial<TParent>;
90
+
91
+ export interface HasOneReferenceApi<TChild extends object = object> {
92
+ load(): Promise<TChild | null>;
93
+ get(): TChild | null;
94
+ set(data: Partial<TChild> | TChild | null): TChild | null;
95
+ }
96
+
97
+ export type HasOneReference<TChild extends object = object> = HasOneReferenceApi<TChild> & Partial<TChild>;
98
+
99
+ export interface ManyToManyCollection<TTarget, TPivot extends object | undefined = undefined> {
100
+ length: number;
101
+ [Symbol.iterator](): Iterator<TTarget>;
82
102
  load(): Promise<TTarget[]>;
83
103
  getItems(): TTarget[];
84
104
  attach(target: TTarget | number | string): void;
85
105
  detach(target: TTarget | number | string): void;
86
106
  syncByIds(ids: (number | string)[]): Promise<void>;
107
+ /** @internal Type-level marker for the related pivot entity */
108
+ readonly __pivotType?: TPivot;
87
109
  }
88
-
89
- export type EntityInstance<
90
- TTable extends TableDef,
91
- TRow = InferRow<TTable>
92
- > = TRow & {
93
- [K in keyof RelationMap<TTable>]:
94
- TTable['relations'][K] extends HasManyRelation<infer TTarget>
95
- ? HasManyCollection<EntityInstance<TTarget>>
96
- : TTable['relations'][K] extends HasOneRelation<infer TTarget>
97
- ? HasOneReference<EntityInstance<TTarget>>
98
- : TTable['relations'][K] extends BelongsToManyRelation<infer TTarget>
99
- ? ManyToManyCollection<EntityInstance<TTarget>>
100
- : TTable['relations'][K] extends BelongsToRelation<infer TTarget>
101
- ? BelongsToReference<EntityInstance<TTarget>>
102
- : never;
103
- } & {
104
- $load<K extends keyof RelationMap<TTable>>(relation: K): Promise<RelationMap<TTable>[K]>;
105
- };
106
-
110
+
111
+ export type EntityInstance<
112
+ TTable extends TableDef,
113
+ TRow = InferRow<TTable>
114
+ > = TRow & {
115
+ [K in keyof RelationMap<TTable>]: RelationWrapper<TTable['relations'][K]>;
116
+ } & {
117
+ $load<K extends keyof RelationMap<TTable>>(relation: K): Promise<RelationMap<TTable>[K]>;
118
+ };
119
+
107
120
  export type Primitive = string | number | boolean | Date | bigint | Buffer | null | undefined;
108
121
 
122
+ type IsAny<T> = 0 extends (1 & T) ? true : false;
123
+
109
124
  export type SelectableKeys<T> = {
110
- [K in keyof T]-?: NonNullable<T[K]> extends Primitive ? K : never
125
+ [K in keyof T]-?: IsAny<T[K]> extends true
126
+ ? never
127
+ : NonNullable<T[K]> extends Primitive
128
+ ? K
129
+ : never
111
130
  }[keyof T];