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.
- package/README.md +23 -13
- package/dist/index.cjs +1750 -733
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +244 -157
- package/dist/index.d.ts +244 -157
- package/dist/index.js +1745 -733
- package/dist/index.js.map +1 -1
- package/package.json +69 -69
- package/src/core/ddl/schema-generator.ts +44 -1
- package/src/decorators/bootstrap.ts +186 -113
- package/src/decorators/column-decorator.ts +8 -49
- package/src/decorators/decorator-metadata.ts +10 -46
- package/src/decorators/entity.ts +30 -40
- package/src/decorators/relations.ts +30 -56
- package/src/orm/entity-hydration.ts +72 -0
- package/src/orm/entity-meta.ts +18 -13
- package/src/orm/entity-metadata.ts +240 -238
- package/src/orm/entity-relation-cache.ts +39 -0
- package/src/orm/entity-relations.ts +207 -0
- package/src/orm/entity.ts +124 -343
- package/src/orm/execute.ts +87 -20
- package/src/orm/lazy-batch/belongs-to-many.ts +134 -0
- package/src/orm/lazy-batch/belongs-to.ts +108 -0
- package/src/orm/lazy-batch/has-many.ts +69 -0
- package/src/orm/lazy-batch/has-one.ts +68 -0
- package/src/orm/lazy-batch/shared.ts +125 -0
- package/src/orm/lazy-batch.ts +4 -309
- package/src/orm/relations/belongs-to.ts +2 -2
- package/src/orm/relations/has-many.ts +23 -9
- package/src/orm/relations/has-one.ts +2 -2
- package/src/orm/relations/many-to-many.ts +29 -14
- package/src/orm/save-graph-types.ts +2 -2
- package/src/orm/save-graph.ts +18 -18
- package/src/query-builder/relation-conditions.ts +80 -59
- package/src/query-builder/relation-cte-builder.ts +63 -0
- package/src/query-builder/relation-filter-utils.ts +159 -0
- package/src/query-builder/relation-include-strategies.ts +177 -0
- package/src/query-builder/relation-join-planner.ts +80 -0
- package/src/query-builder/relation-service.ts +103 -159
- package/src/query-builder/relation-types.ts +43 -12
- package/src/query-builder/select/projection-facet.ts +23 -23
- package/src/query-builder/select/select-operations.ts +145 -0
- package/src/query-builder/select.ts +373 -426
- package/src/schema/relation.ts +22 -18
- package/src/schema/table.ts +22 -9
- package/src/schema/types.ts +103 -84
|
@@ -1,34 +1,5 @@
|
|
|
1
1
|
import { ColumnDefLike, RelationMetadata } from '../orm/entity-metadata.js';
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* Context object provided by standard decorators in newer TypeScript versions.
|
|
5
|
-
*/
|
|
6
|
-
export interface StandardDecoratorContext {
|
|
7
|
-
kind: string;
|
|
8
|
-
name?: string | symbol;
|
|
9
|
-
metadata?: Record<PropertyKey, unknown>;
|
|
10
|
-
static?: boolean;
|
|
11
|
-
private?: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Dual-mode property decorator that supports both legacy and standard decorator syntax.
|
|
16
|
-
*/
|
|
17
|
-
export interface DualModePropertyDecorator {
|
|
18
|
-
(target: object, propertyKey: string | symbol): void;
|
|
19
|
-
(value: unknown, context: StandardDecoratorContext): void;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Dual-mode class decorator that supports both legacy and standard decorator syntax.
|
|
24
|
-
*/
|
|
25
|
-
export interface DualModeClassDecorator {
|
|
26
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
27
|
-
<TFunction extends Function>(value: TFunction): void | TFunction;
|
|
28
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
29
|
-
<TFunction extends Function>(value: TFunction, context: StandardDecoratorContext): void | TFunction;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
3
|
/**
|
|
33
4
|
* Bag for storing decorator metadata during the decoration phase.
|
|
34
5
|
*/
|
|
@@ -39,37 +10,31 @@ export interface DecoratorMetadataBag {
|
|
|
39
10
|
|
|
40
11
|
const METADATA_KEY = 'metal-orm:decorators';
|
|
41
12
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
* @param value - The value to check.
|
|
45
|
-
* @returns True if the value is a StandardDecoratorContext.
|
|
46
|
-
*/
|
|
47
|
-
export const isStandardDecoratorContext = (value: unknown): value is StandardDecoratorContext => {
|
|
48
|
-
return typeof value === 'object' && value !== null && 'kind' in (value as object);
|
|
13
|
+
type MetadataCarrier = {
|
|
14
|
+
metadata?: Record<PropertyKey, unknown>;
|
|
49
15
|
};
|
|
50
16
|
|
|
51
17
|
/**
|
|
52
18
|
* Gets or creates a metadata bag for the given decorator context.
|
|
53
|
-
* @param context - The decorator context.
|
|
19
|
+
* @param context - The decorator context with metadata support.
|
|
54
20
|
* @returns The metadata bag.
|
|
55
21
|
*/
|
|
56
|
-
export const getOrCreateMetadataBag = (context:
|
|
22
|
+
export const getOrCreateMetadataBag = (context: MetadataCarrier): DecoratorMetadataBag => {
|
|
57
23
|
const metadata = context.metadata || (context.metadata = {} as Record<PropertyKey, unknown>);
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
|
|
24
|
+
let bag = metadata[METADATA_KEY] as DecoratorMetadataBag | undefined;
|
|
25
|
+
if (!bag) {
|
|
26
|
+
bag = { columns: [], relations: [] };
|
|
27
|
+
metadata[METADATA_KEY] = bag;
|
|
61
28
|
}
|
|
62
|
-
const bag: DecoratorMetadataBag = { columns: [], relations: [] };
|
|
63
|
-
metadata[METADATA_KEY] = bag;
|
|
64
29
|
return bag;
|
|
65
30
|
};
|
|
66
31
|
|
|
67
32
|
/**
|
|
68
33
|
* Reads the metadata bag from the given decorator context.
|
|
69
|
-
* @param context - The decorator context.
|
|
34
|
+
* @param context - The decorator context with metadata support.
|
|
70
35
|
* @returns The metadata bag if present.
|
|
71
36
|
*/
|
|
72
|
-
export const readMetadataBag = (context:
|
|
37
|
+
export const readMetadataBag = (context: MetadataCarrier): DecoratorMetadataBag | undefined => {
|
|
73
38
|
return context.metadata?.[METADATA_KEY] as DecoratorMetadataBag | undefined;
|
|
74
39
|
};
|
|
75
40
|
|
|
@@ -87,7 +52,6 @@ export const readMetadataBagFromConstructor = (ctor: object): DecoratorMetadataB
|
|
|
87
52
|
|
|
88
53
|
/**
|
|
89
54
|
* Public helper to read decorator metadata from a class constructor.
|
|
90
|
-
* Standard decorators only; legacy metadata is intentionally ignored.
|
|
91
55
|
* @param ctor - The entity constructor.
|
|
92
56
|
* @returns The metadata bag if present.
|
|
93
57
|
*/
|
package/src/decorators/entity.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
ensureEntityMetadata,
|
|
8
8
|
setEntityTableName
|
|
9
9
|
} from '../orm/entity-metadata.js';
|
|
10
|
-
import {
|
|
10
|
+
import { readMetadataBag } from './decorator-metadata.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Options for defining an entity.
|
|
@@ -43,51 +43,41 @@ const deriveTableNameFromConstructor = (ctor: EntityConstructor<unknown>): strin
|
|
|
43
43
|
* @returns A class decorator that registers the entity metadata.
|
|
44
44
|
*/
|
|
45
45
|
export function Entity(options: EntityOptions = {}) {
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
46
|
+
return function <T extends EntityConstructor>(value: T, context: ClassDecoratorContext): T {
|
|
47
|
+
const ctor = value;
|
|
48
|
+
const tableName = options.tableName ?? deriveTableNameFromConstructor(ctor);
|
|
49
|
+
setEntityTableName(ctor, tableName, options.hooks);
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const bag = readMetadataBag(context);
|
|
59
|
-
if (bag) {
|
|
60
|
-
const meta = ensureEntityMetadata(ctor);
|
|
61
|
-
for (const entry of bag.columns) {
|
|
62
|
-
if (meta.columns[entry.propertyName]) {
|
|
63
|
-
throw new Error(
|
|
64
|
-
`Column '${entry.propertyName}' is already defined on entity '${ctor.name}'.`
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
addColumnMetadata(ctor, entry.propertyName, { ...entry.column });
|
|
51
|
+
const bag = readMetadataBag(context);
|
|
52
|
+
if (bag) {
|
|
53
|
+
const meta = ensureEntityMetadata(ctor);
|
|
54
|
+
for (const entry of bag.columns) {
|
|
55
|
+
if (meta.columns[entry.propertyName]) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Column '${entry.propertyName}' is already defined on entity '${ctor.name}'.`
|
|
58
|
+
);
|
|
68
59
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
entry.relation.kind === RelationKinds.BelongsToMany
|
|
77
|
-
? {
|
|
78
|
-
...entry.relation,
|
|
79
|
-
defaultPivotColumns: entry.relation.defaultPivotColumns
|
|
80
|
-
? [...entry.relation.defaultPivotColumns]
|
|
81
|
-
: undefined
|
|
82
|
-
}
|
|
83
|
-
: { ...entry.relation };
|
|
84
|
-
addRelationMetadata(ctor, entry.propertyName, relationCopy);
|
|
60
|
+
addColumnMetadata(ctor, entry.propertyName, { ...entry.column });
|
|
61
|
+
}
|
|
62
|
+
for (const entry of bag.relations) {
|
|
63
|
+
if (meta.relations[entry.propertyName]) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
`Relation '${entry.propertyName}' is already defined on entity '${ctor.name}'.`
|
|
66
|
+
);
|
|
85
67
|
}
|
|
68
|
+
const relationCopy =
|
|
69
|
+
entry.relation.kind === RelationKinds.BelongsToMany
|
|
70
|
+
? {
|
|
71
|
+
...entry.relation,
|
|
72
|
+
defaultPivotColumns: entry.relation.defaultPivotColumns
|
|
73
|
+
? [...entry.relation.defaultPivotColumns]
|
|
74
|
+
: undefined
|
|
75
|
+
}
|
|
76
|
+
: { ...entry.relation };
|
|
77
|
+
addRelationMetadata(ctor, entry.propertyName, relationCopy);
|
|
86
78
|
}
|
|
87
79
|
}
|
|
88
80
|
|
|
89
81
|
return ctor;
|
|
90
82
|
};
|
|
91
|
-
|
|
92
|
-
return decoratorWithContext;
|
|
93
83
|
}
|
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import { CascadeMode, RelationKinds } from '../schema/relation.js';
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
EntityConstructor,
|
|
3
|
+
EntityOrTableTarget,
|
|
5
4
|
EntityOrTableTargetResolver,
|
|
6
5
|
RelationMetadata
|
|
7
6
|
} from '../orm/entity-metadata.js';
|
|
8
|
-
import {
|
|
9
|
-
DualModePropertyDecorator,
|
|
10
|
-
getOrCreateMetadataBag,
|
|
11
|
-
isStandardDecoratorContext,
|
|
12
|
-
StandardDecoratorContext
|
|
13
|
-
} from './decorator-metadata.js';
|
|
7
|
+
import { getOrCreateMetadataBag } from './decorator-metadata.js';
|
|
14
8
|
|
|
15
9
|
interface BaseRelationOptions {
|
|
16
10
|
target: EntityOrTableTargetResolver;
|
|
@@ -22,31 +16,34 @@ interface BaseRelationOptions {
|
|
|
22
16
|
* Options for HasMany relation.
|
|
23
17
|
*/
|
|
24
18
|
export interface HasManyOptions extends BaseRelationOptions {
|
|
25
|
-
foreignKey
|
|
19
|
+
foreignKey?: string;
|
|
26
20
|
}
|
|
27
21
|
|
|
28
22
|
/**
|
|
29
23
|
* Options for HasOne relation.
|
|
30
24
|
*/
|
|
31
25
|
export interface HasOneOptions extends BaseRelationOptions {
|
|
32
|
-
foreignKey
|
|
26
|
+
foreignKey?: string;
|
|
33
27
|
}
|
|
34
28
|
|
|
35
29
|
/**
|
|
36
30
|
* Options for BelongsTo relation.
|
|
37
31
|
*/
|
|
38
32
|
export interface BelongsToOptions extends BaseRelationOptions {
|
|
39
|
-
foreignKey
|
|
33
|
+
foreignKey?: string;
|
|
40
34
|
}
|
|
41
35
|
|
|
42
36
|
/**
|
|
43
37
|
* Options for BelongsToMany relation.
|
|
44
38
|
*/
|
|
45
|
-
export interface BelongsToManyOptions
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
39
|
+
export interface BelongsToManyOptions<
|
|
40
|
+
TTarget extends EntityOrTableTarget = EntityOrTableTarget,
|
|
41
|
+
TPivot extends EntityOrTableTarget = EntityOrTableTarget
|
|
42
|
+
> {
|
|
43
|
+
target: EntityOrTableTargetResolver<TTarget>;
|
|
44
|
+
pivotTable: EntityOrTableTargetResolver<TPivot>;
|
|
45
|
+
pivotForeignKeyToRoot?: string;
|
|
46
|
+
pivotForeignKeyToTarget?: string;
|
|
50
47
|
localKey?: string;
|
|
51
48
|
targetKey?: string;
|
|
52
49
|
pivotPrimaryKey?: string;
|
|
@@ -61,48 +58,22 @@ const normalizePropertyName = (name: string | symbol): string => {
|
|
|
61
58
|
return name;
|
|
62
59
|
};
|
|
63
60
|
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (instanceOrCtor && typeof (instanceOrCtor as { constructor: new (...args: unknown[]) => unknown }).constructor === 'function') {
|
|
69
|
-
return (instanceOrCtor as { constructor: new (...args: unknown[]) => unknown }).constructor as EntityConstructor;
|
|
70
|
-
}
|
|
71
|
-
return undefined;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
const registerRelation = (ctor: EntityConstructor, propertyName: string, metadata: RelationMetadata): void => {
|
|
75
|
-
addRelationMetadata(ctor, propertyName, metadata);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const createFieldDecorator = (
|
|
79
|
-
metadataFactory: (propertyName: string) => RelationMetadata
|
|
80
|
-
) => {
|
|
81
|
-
const decorator: DualModePropertyDecorator = (targetOrValue, propertyKeyOrContext) => {
|
|
82
|
-
if (isStandardDecoratorContext(propertyKeyOrContext)) {
|
|
83
|
-
const ctx = propertyKeyOrContext as StandardDecoratorContext;
|
|
84
|
-
if (!ctx.name) {
|
|
85
|
-
throw new Error('Relation decorator requires a property name');
|
|
86
|
-
}
|
|
87
|
-
const propertyName = normalizePropertyName(ctx.name);
|
|
88
|
-
const bag = getOrCreateMetadataBag(ctx);
|
|
89
|
-
const relationMetadata = metadataFactory(propertyName);
|
|
90
|
-
|
|
91
|
-
if (!bag.relations.some(entry => entry.propertyName === propertyName)) {
|
|
92
|
-
bag.relations.push({ propertyName, relation: relationMetadata });
|
|
93
|
-
}
|
|
94
|
-
return;
|
|
61
|
+
const createFieldDecorator = (metadataFactory: (propertyName: string) => RelationMetadata) => {
|
|
62
|
+
return function (_value: unknown, context: ClassFieldDecoratorContext) {
|
|
63
|
+
if (!context.name) {
|
|
64
|
+
throw new Error('Relation decorator requires a property name');
|
|
95
65
|
}
|
|
66
|
+
if (context.private) {
|
|
67
|
+
throw new Error('Relation decorator does not support private fields');
|
|
68
|
+
}
|
|
69
|
+
const propertyName = normalizePropertyName(context.name);
|
|
70
|
+
const bag = getOrCreateMetadataBag(context);
|
|
71
|
+
const relationMetadata = metadataFactory(propertyName);
|
|
96
72
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (!ctor) {
|
|
100
|
-
throw new Error('Unable to resolve constructor when registering relation metadata');
|
|
73
|
+
if (!bag.relations.some(entry => entry.propertyName === propertyName)) {
|
|
74
|
+
bag.relations.push({ propertyName, relation: relationMetadata });
|
|
101
75
|
}
|
|
102
|
-
registerRelation(ctor, propertyName, metadataFactory(propertyName));
|
|
103
76
|
};
|
|
104
|
-
|
|
105
|
-
return decorator;
|
|
106
77
|
};
|
|
107
78
|
|
|
108
79
|
/**
|
|
@@ -147,7 +118,7 @@ export function BelongsTo(options: BelongsToOptions) {
|
|
|
147
118
|
kind: RelationKinds.BelongsTo,
|
|
148
119
|
propertyKey: propertyName,
|
|
149
120
|
target: options.target,
|
|
150
|
-
foreignKey: options.foreignKey
|
|
121
|
+
foreignKey: options.foreignKey ?? `${propertyName}_id`,
|
|
151
122
|
localKey: options.localKey,
|
|
152
123
|
cascade: options.cascade
|
|
153
124
|
}));
|
|
@@ -158,7 +129,10 @@ export function BelongsTo(options: BelongsToOptions) {
|
|
|
158
129
|
* @param options - The relation options.
|
|
159
130
|
* @returns A property decorator that registers the relation metadata.
|
|
160
131
|
*/
|
|
161
|
-
export function BelongsToMany
|
|
132
|
+
export function BelongsToMany<
|
|
133
|
+
TTarget extends EntityOrTableTarget = EntityOrTableTarget,
|
|
134
|
+
TPivot extends EntityOrTableTarget = EntityOrTableTarget
|
|
135
|
+
>(options: BelongsToManyOptions<TTarget, TPivot>) {
|
|
162
136
|
return createFieldDecorator(propertyName => ({
|
|
163
137
|
kind: RelationKinds.BelongsToMany,
|
|
164
138
|
propertyKey: propertyName,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { TableDef } from '../schema/table.js';
|
|
2
|
+
import { RelationKinds } from '../schema/relation.js';
|
|
3
|
+
import { findPrimaryKey } from '../query-builder/hydration-planner.js';
|
|
4
|
+
import { EntityMeta } from './entity-meta.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Type representing an array of database rows.
|
|
8
|
+
*/
|
|
9
|
+
type Rows = Record<string, unknown>[];
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Converts a value to a string key.
|
|
13
|
+
* @param value - The value to convert
|
|
14
|
+
* @returns String representation of the value
|
|
15
|
+
*/
|
|
16
|
+
const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Populates the hydration cache with relation data from the database row.
|
|
20
|
+
* @template TTable - The table type
|
|
21
|
+
* @param entity - The entity instance
|
|
22
|
+
* @param row - The database row
|
|
23
|
+
* @param meta - The entity metadata
|
|
24
|
+
*/
|
|
25
|
+
export const populateHydrationCache = <TTable extends TableDef>(
|
|
26
|
+
entity: Record<string, unknown>,
|
|
27
|
+
row: Record<string, unknown>,
|
|
28
|
+
meta: EntityMeta<TTable>
|
|
29
|
+
): void => {
|
|
30
|
+
for (const relationName of Object.keys(meta.table.relations)) {
|
|
31
|
+
const relation = meta.table.relations[relationName];
|
|
32
|
+
const data = row[relationName];
|
|
33
|
+
if (relation.type === RelationKinds.HasOne) {
|
|
34
|
+
const localKey = relation.localKey || findPrimaryKey(meta.table);
|
|
35
|
+
const rootValue = entity[localKey];
|
|
36
|
+
if (rootValue === undefined || rootValue === null) continue;
|
|
37
|
+
if (!data || typeof data !== 'object') continue;
|
|
38
|
+
const cache = new Map<string, Record<string, unknown>>();
|
|
39
|
+
cache.set(toKey(rootValue), data as Record<string, unknown>);
|
|
40
|
+
meta.relationHydration.set(relationName, cache);
|
|
41
|
+
meta.relationCache.set(relationName, Promise.resolve(cache));
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!Array.isArray(data)) continue;
|
|
46
|
+
|
|
47
|
+
if (relation.type === RelationKinds.HasMany || relation.type === RelationKinds.BelongsToMany) {
|
|
48
|
+
const localKey = relation.localKey || findPrimaryKey(meta.table);
|
|
49
|
+
const rootValue = entity[localKey];
|
|
50
|
+
if (rootValue === undefined || rootValue === null) continue;
|
|
51
|
+
const cache = new Map<string, Rows>();
|
|
52
|
+
cache.set(toKey(rootValue), data as Rows);
|
|
53
|
+
meta.relationHydration.set(relationName, cache);
|
|
54
|
+
meta.relationCache.set(relationName, Promise.resolve(cache));
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (relation.type === RelationKinds.BelongsTo) {
|
|
59
|
+
const targetKey = relation.localKey || findPrimaryKey(relation.target);
|
|
60
|
+
const cache = new Map<string, Record<string, unknown>>();
|
|
61
|
+
for (const item of data) {
|
|
62
|
+
const pkValue = item[targetKey];
|
|
63
|
+
if (pkValue === undefined || pkValue === null) continue;
|
|
64
|
+
cache.set(toKey(pkValue), item);
|
|
65
|
+
}
|
|
66
|
+
if (cache.size) {
|
|
67
|
+
meta.relationHydration.set(relationName, cache);
|
|
68
|
+
meta.relationCache.set(relationName, Promise.resolve(cache));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
package/src/orm/entity-meta.ts
CHANGED
|
@@ -1,25 +1,30 @@
|
|
|
1
|
-
import { TableDef } from '../schema/table.js';
|
|
1
|
+
import { TableDef } from '../schema/table.js';
|
|
2
|
+
import { RelationIncludeOptions } from '../query-builder/relation-types.js';
|
|
2
3
|
import { EntityContext } from './entity-context.js';
|
|
3
|
-
import { RelationMap } from '../schema/types.js';
|
|
4
|
+
import { RelationMap } from '../schema/types.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Symbol used to store entity metadata on entity instances
|
|
7
8
|
*/
|
|
8
|
-
export const ENTITY_META = Symbol('EntityMeta');
|
|
9
|
-
|
|
10
|
-
const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
export const ENTITY_META = Symbol('EntityMeta');
|
|
10
|
+
|
|
11
|
+
const toKey = (value: unknown): string => (value === null || value === undefined ? '' : String(value));
|
|
12
|
+
|
|
13
|
+
export type RelationKey<TTable extends TableDef> = Extract<keyof RelationMap<TTable>, string>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Metadata stored on entity instances for ORM internal use
|
|
17
|
+
* @typeParam TTable - Table definition type
|
|
18
|
+
*/
|
|
19
|
+
export interface EntityMeta<TTable extends TableDef> {
|
|
17
20
|
/** Entity context */
|
|
18
21
|
ctx: EntityContext;
|
|
19
22
|
/** Table definition */
|
|
20
23
|
table: TTable;
|
|
21
|
-
/** Relations that should be loaded lazily */
|
|
22
|
-
lazyRelations:
|
|
24
|
+
/** Relations that should be loaded lazily */
|
|
25
|
+
lazyRelations: RelationKey<TTable>[];
|
|
26
|
+
/** Include options for lazy relations */
|
|
27
|
+
lazyRelationOptions: Map<string, RelationIncludeOptions>;
|
|
23
28
|
/** Cache for relation promises */
|
|
24
29
|
relationCache: Map<string, Promise<unknown>>;
|
|
25
30
|
/** Hydration data for relations */
|