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,159 +1,159 @@
|
|
|
1
|
-
import { EntityConstructor } from './entity-metadata.js';
|
|
2
|
-
import { rebuildRegistry } from './entity-registry.js';
|
|
3
|
-
import { hasEntityMeta } from './entity-meta.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Strategy interface for materializing entity instances (Open/Closed Principle).
|
|
7
|
-
*/
|
|
8
|
-
export interface EntityMaterializationStrategy {
|
|
9
|
-
/**
|
|
10
|
-
* Creates an instance of the entity class and populates it with data.
|
|
11
|
-
* @param ctor - The entity constructor
|
|
12
|
-
* @param data - The raw data to populate
|
|
13
|
-
* @returns The materialized entity instance
|
|
14
|
-
*/
|
|
15
|
-
materialize<T>(ctor: EntityConstructor<T>, data: Record<string, unknown>): T;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Default strategy: Uses Object.create() to create instance without calling constructor.
|
|
20
|
-
* Safe for classes with required constructor parameters.
|
|
21
|
-
*/
|
|
22
|
-
export class PrototypeMaterializationStrategy implements EntityMaterializationStrategy {
|
|
23
|
-
materialize<T>(ctor: EntityConstructor<T>, data: Record<string, unknown>): T {
|
|
24
|
-
const instance = Object.create(ctor.prototype) as T;
|
|
25
|
-
Object.assign(instance, data);
|
|
26
|
-
return instance;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Alternative strategy: Calls default constructor then assigns properties.
|
|
32
|
-
* Use when class constructor initializes important state.
|
|
33
|
-
*/
|
|
34
|
-
export class ConstructorMaterializationStrategy implements EntityMaterializationStrategy {
|
|
35
|
-
materialize<T>(ctor: EntityConstructor<T>, data: Record<string, unknown>): T {
|
|
36
|
-
const instance = Reflect.construct(ctor, []) as T & Record<string, unknown>;
|
|
37
|
-
Object.assign(instance, data);
|
|
38
|
-
return instance;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Interface for materializing query results into real entity class instances.
|
|
44
|
-
*/
|
|
45
|
-
export interface EntityMaterializer {
|
|
46
|
-
/**
|
|
47
|
-
* Materializes a single row into a real entity instance.
|
|
48
|
-
* @param entityClass - The entity constructor
|
|
49
|
-
* @param row - The raw data row
|
|
50
|
-
* @returns The materialized entity instance
|
|
51
|
-
*/
|
|
52
|
-
materialize<T>(entityClass: EntityConstructor<T>, row: Record<string, unknown>): T;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Materializes multiple rows into real entity instances.
|
|
56
|
-
* @param entityClass - The entity constructor
|
|
57
|
-
* @param rows - The raw data rows
|
|
58
|
-
* @returns Array of materialized entity instances
|
|
59
|
-
*/
|
|
60
|
-
materializeMany<T>(entityClass: EntityConstructor<T>, rows: Record<string, unknown>[]): T[];
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Default implementation of EntityMaterializer.
|
|
65
|
-
* Converts query results into actual class instances with working methods.
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* const materializer = new DefaultEntityMaterializer();
|
|
69
|
-
* const users = materializer.materializeMany(User, queryResults);
|
|
70
|
-
* users[0] instanceof User; // true
|
|
71
|
-
* users[0].getFullName(); // works!
|
|
72
|
-
*/
|
|
73
|
-
export class DefaultEntityMaterializer implements EntityMaterializer {
|
|
74
|
-
constructor(
|
|
75
|
-
private readonly strategy: EntityMaterializationStrategy = new ConstructorMaterializationStrategy()
|
|
76
|
-
) { }
|
|
77
|
-
|
|
78
|
-
materialize<T>(ctor: EntityConstructor<T>, row: Record<string, unknown>): T {
|
|
79
|
-
if (hasEntityMeta(row)) {
|
|
80
|
-
return this.materializeEntityProxy(ctor, row);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const instance = this.strategy.materialize(ctor, row);
|
|
84
|
-
this.materializeRelations(instance as Record<string, unknown>);
|
|
85
|
-
return instance;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
materializeMany<T>(ctor: EntityConstructor<T>, rows: Record<string, unknown>[]): T[] {
|
|
89
|
-
return rows.map(row => this.materialize(ctor, row));
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Recursively materializes nested relation data.
|
|
94
|
-
*/
|
|
95
|
-
private materializeRelations(instance: Record<string, unknown>): void {
|
|
96
|
-
// Rebuild registry to ensure we have latest metadata
|
|
97
|
-
rebuildRegistry();
|
|
98
|
-
|
|
99
|
-
for (const value of Object.values(instance)) {
|
|
100
|
-
if (value === null || value === undefined) continue;
|
|
101
|
-
|
|
102
|
-
// Handle has-one / belongs-to (single object)
|
|
103
|
-
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
104
|
-
const nested = value as Record<string, unknown>;
|
|
105
|
-
// Check if this looks like an entity (has common entity patterns)
|
|
106
|
-
if (this.isEntityLike(nested)) {
|
|
107
|
-
// For now, keep as-is since we don't have relation metadata here
|
|
108
|
-
// Future: use relation metadata to get target constructor
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Handle has-many / belongs-to-many (array)
|
|
113
|
-
if (Array.isArray(value) && value.length > 0) {
|
|
114
|
-
const first = value[0];
|
|
115
|
-
if (typeof first === 'object' && first !== null && this.isEntityLike(first)) {
|
|
116
|
-
// For now, keep array as-is
|
|
117
|
-
// Future: materialize each item if we can resolve the target class
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Simple heuristic to check if an object looks like an entity.
|
|
125
|
-
*/
|
|
126
|
-
private isEntityLike(obj: Record<string, unknown>): boolean {
|
|
127
|
-
return 'id' in obj || Object.keys(obj).some(k =>
|
|
128
|
-
k.endsWith('Id') || k === 'createdAt' || k === 'updatedAt'
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
private materializeEntityProxy<T>(ctor: EntityConstructor<T>, row: Record<string, unknown>): T {
|
|
133
|
-
const proxy = row as Record<string, unknown>;
|
|
134
|
-
const baseline = this.strategy.materialize(ctor, {}) as Record<string, unknown>;
|
|
135
|
-
for (const key of Object.keys(baseline)) {
|
|
136
|
-
if (!Object.prototype.hasOwnProperty.call(proxy, key)) {
|
|
137
|
-
proxy[key] = baseline[key];
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
Object.setPrototypeOf(proxy, ctor.prototype);
|
|
141
|
-
return proxy as T;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Convenience function to materialize query results as real class instances.
|
|
147
|
-
*
|
|
148
|
-
* @example
|
|
149
|
-
* const results = await selectFromEntity(User).execute(session);
|
|
150
|
-
* const users = materializeAs(User, results);
|
|
151
|
-
* users[0] instanceof User; // true!
|
|
152
|
-
*/
|
|
153
|
-
export const materializeAs = <TEntity extends object>(
|
|
154
|
-
ctor: EntityConstructor<TEntity>,
|
|
155
|
-
results: Record<string, unknown>[]
|
|
156
|
-
): TEntity[] => {
|
|
157
|
-
const materializer = new DefaultEntityMaterializer();
|
|
158
|
-
return materializer.materializeMany(ctor, results);
|
|
159
|
-
};
|
|
1
|
+
import { EntityConstructor } from './entity-metadata.js';
|
|
2
|
+
import { rebuildRegistry } from './entity-registry.js';
|
|
3
|
+
import { hasEntityMeta } from './entity-meta.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Strategy interface for materializing entity instances (Open/Closed Principle).
|
|
7
|
+
*/
|
|
8
|
+
export interface EntityMaterializationStrategy {
|
|
9
|
+
/**
|
|
10
|
+
* Creates an instance of the entity class and populates it with data.
|
|
11
|
+
* @param ctor - The entity constructor
|
|
12
|
+
* @param data - The raw data to populate
|
|
13
|
+
* @returns The materialized entity instance
|
|
14
|
+
*/
|
|
15
|
+
materialize<T>(ctor: EntityConstructor<T>, data: Record<string, unknown>): T;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Default strategy: Uses Object.create() to create instance without calling constructor.
|
|
20
|
+
* Safe for classes with required constructor parameters.
|
|
21
|
+
*/
|
|
22
|
+
export class PrototypeMaterializationStrategy implements EntityMaterializationStrategy {
|
|
23
|
+
materialize<T>(ctor: EntityConstructor<T>, data: Record<string, unknown>): T {
|
|
24
|
+
const instance = Object.create(ctor.prototype) as T;
|
|
25
|
+
Object.assign(instance, data);
|
|
26
|
+
return instance;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Alternative strategy: Calls default constructor then assigns properties.
|
|
32
|
+
* Use when class constructor initializes important state.
|
|
33
|
+
*/
|
|
34
|
+
export class ConstructorMaterializationStrategy implements EntityMaterializationStrategy {
|
|
35
|
+
materialize<T>(ctor: EntityConstructor<T>, data: Record<string, unknown>): T {
|
|
36
|
+
const instance = Reflect.construct(ctor, []) as T & Record<string, unknown>;
|
|
37
|
+
Object.assign(instance, data);
|
|
38
|
+
return instance;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Interface for materializing query results into real entity class instances.
|
|
44
|
+
*/
|
|
45
|
+
export interface EntityMaterializer {
|
|
46
|
+
/**
|
|
47
|
+
* Materializes a single row into a real entity instance.
|
|
48
|
+
* @param entityClass - The entity constructor
|
|
49
|
+
* @param row - The raw data row
|
|
50
|
+
* @returns The materialized entity instance
|
|
51
|
+
*/
|
|
52
|
+
materialize<T>(entityClass: EntityConstructor<T>, row: Record<string, unknown>): T;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Materializes multiple rows into real entity instances.
|
|
56
|
+
* @param entityClass - The entity constructor
|
|
57
|
+
* @param rows - The raw data rows
|
|
58
|
+
* @returns Array of materialized entity instances
|
|
59
|
+
*/
|
|
60
|
+
materializeMany<T>(entityClass: EntityConstructor<T>, rows: Record<string, unknown>[]): T[];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Default implementation of EntityMaterializer.
|
|
65
|
+
* Converts query results into actual class instances with working methods.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* const materializer = new DefaultEntityMaterializer();
|
|
69
|
+
* const users = materializer.materializeMany(User, queryResults);
|
|
70
|
+
* users[0] instanceof User; // true
|
|
71
|
+
* users[0].getFullName(); // works!
|
|
72
|
+
*/
|
|
73
|
+
export class DefaultEntityMaterializer implements EntityMaterializer {
|
|
74
|
+
constructor(
|
|
75
|
+
private readonly strategy: EntityMaterializationStrategy = new ConstructorMaterializationStrategy()
|
|
76
|
+
) { }
|
|
77
|
+
|
|
78
|
+
materialize<T>(ctor: EntityConstructor<T>, row: Record<string, unknown>): T {
|
|
79
|
+
if (hasEntityMeta(row)) {
|
|
80
|
+
return this.materializeEntityProxy(ctor, row);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const instance = this.strategy.materialize(ctor, row);
|
|
84
|
+
this.materializeRelations(instance as Record<string, unknown>);
|
|
85
|
+
return instance;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
materializeMany<T>(ctor: EntityConstructor<T>, rows: Record<string, unknown>[]): T[] {
|
|
89
|
+
return rows.map(row => this.materialize(ctor, row));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Recursively materializes nested relation data.
|
|
94
|
+
*/
|
|
95
|
+
private materializeRelations(instance: Record<string, unknown>): void {
|
|
96
|
+
// Rebuild registry to ensure we have latest metadata
|
|
97
|
+
rebuildRegistry();
|
|
98
|
+
|
|
99
|
+
for (const value of Object.values(instance)) {
|
|
100
|
+
if (value === null || value === undefined) continue;
|
|
101
|
+
|
|
102
|
+
// Handle has-one / belongs-to (single object)
|
|
103
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
104
|
+
const nested = value as Record<string, unknown>;
|
|
105
|
+
// Check if this looks like an entity (has common entity patterns)
|
|
106
|
+
if (this.isEntityLike(nested)) {
|
|
107
|
+
// For now, keep as-is since we don't have relation metadata here
|
|
108
|
+
// Future: use relation metadata to get target constructor
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Handle has-many / belongs-to-many (array)
|
|
113
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
114
|
+
const first = value[0];
|
|
115
|
+
if (typeof first === 'object' && first !== null && this.isEntityLike(first)) {
|
|
116
|
+
// For now, keep array as-is
|
|
117
|
+
// Future: materialize each item if we can resolve the target class
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Simple heuristic to check if an object looks like an entity.
|
|
125
|
+
*/
|
|
126
|
+
private isEntityLike(obj: Record<string, unknown>): boolean {
|
|
127
|
+
return 'id' in obj || Object.keys(obj).some(k =>
|
|
128
|
+
k.endsWith('Id') || k === 'createdAt' || k === 'updatedAt'
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private materializeEntityProxy<T>(ctor: EntityConstructor<T>, row: Record<string, unknown>): T {
|
|
133
|
+
const proxy = row as Record<string, unknown>;
|
|
134
|
+
const baseline = this.strategy.materialize(ctor, {}) as Record<string, unknown>;
|
|
135
|
+
for (const key of Object.keys(baseline)) {
|
|
136
|
+
if (!Object.prototype.hasOwnProperty.call(proxy, key)) {
|
|
137
|
+
proxy[key] = baseline[key];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
Object.setPrototypeOf(proxy, ctor.prototype);
|
|
141
|
+
return proxy as T;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Convenience function to materialize query results as real class instances.
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* const results = await selectFromEntity(User).execute(session);
|
|
150
|
+
* const users = materializeAs(User, results);
|
|
151
|
+
* users[0] instanceof User; // true!
|
|
152
|
+
*/
|
|
153
|
+
export const materializeAs = <TEntity extends object>(
|
|
154
|
+
ctor: EntityConstructor<TEntity>,
|
|
155
|
+
results: Record<string, unknown>[]
|
|
156
|
+
): TEntity[] => {
|
|
157
|
+
const materializer = new DefaultEntityMaterializer();
|
|
158
|
+
return materializer.materializeMany(ctor, results);
|
|
159
|
+
};
|
package/src/orm/entity-meta.ts
CHANGED
|
@@ -1,97 +1,97 @@
|
|
|
1
|
-
import { TableDef } from '../schema/table.js';
|
|
2
|
-
import { RelationIncludeOptions } from '../query-builder/relation-types.js';
|
|
3
|
-
import { EntityContext } from './entity-context.js';
|
|
4
|
-
import { RelationMap } from '../schema/types.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Symbol used to store entity metadata on entity instances
|
|
8
|
-
*/
|
|
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> {
|
|
20
|
-
/** Entity context */
|
|
21
|
-
ctx: EntityContext;
|
|
22
|
-
/** Table definition */
|
|
23
|
-
table: TTable;
|
|
24
|
-
/** Relations that should be loaded lazily */
|
|
25
|
-
lazyRelations: RelationKey<TTable>[];
|
|
26
|
-
/** Include options for lazy relations */
|
|
27
|
-
lazyRelationOptions: Map<string, RelationIncludeOptions>;
|
|
28
|
-
/** Cache for relation promises */
|
|
29
|
-
relationCache: Map<string, Promise<unknown>>;
|
|
30
|
-
/** Hydration data for relations */
|
|
31
|
-
relationHydration: Map<string, Map<string, unknown>>;
|
|
32
|
-
/** Relation wrapper instances */
|
|
33
|
-
relationWrappers: Map<string, unknown>;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Gets hydration rows for a specific relation and key
|
|
38
|
-
* @param meta - Entity metadata
|
|
39
|
-
* @param relationName - Name of the relation
|
|
40
|
-
* @param key - Key to look up in the hydration map
|
|
41
|
-
* @returns Array of hydration rows or undefined if not found
|
|
42
|
-
* @typeParam TTable - Table definition type
|
|
43
|
-
*/
|
|
44
|
-
export const getHydrationRows = <TTable extends TableDef>(
|
|
45
|
-
meta: EntityMeta<TTable>,
|
|
46
|
-
relationName: string,
|
|
47
|
-
key: unknown
|
|
48
|
-
): Record<string, unknown>[] | undefined => {
|
|
49
|
-
const map = meta.relationHydration.get(relationName);
|
|
50
|
-
if (!map) return undefined;
|
|
51
|
-
const rows = map.get(toKey(key));
|
|
52
|
-
if (!rows) return undefined;
|
|
53
|
-
return Array.isArray(rows) ? rows : undefined;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Gets a single hydration record for a specific relation and key
|
|
58
|
-
* @param meta - Entity metadata
|
|
59
|
-
* @param relationName - Name of the relation
|
|
60
|
-
* @param key - Key to look up in the hydration map
|
|
61
|
-
* @returns Single hydration record or undefined if not found
|
|
62
|
-
* @typeParam TTable - Table definition type
|
|
63
|
-
*/
|
|
64
|
-
export const getHydrationRecord = <TTable extends TableDef>(
|
|
65
|
-
meta: EntityMeta<TTable>,
|
|
66
|
-
relationName: string,
|
|
67
|
-
key: unknown
|
|
68
|
-
): Record<string, unknown> | undefined => {
|
|
69
|
-
const map = meta.relationHydration.get(relationName);
|
|
70
|
-
if (!map) return undefined;
|
|
71
|
-
const value = map.get(toKey(key));
|
|
72
|
-
if (!value) return undefined;
|
|
73
|
-
if (Array.isArray(value)) {
|
|
74
|
-
return value[0];
|
|
75
|
-
}
|
|
76
|
-
return value as Record<string, unknown>;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Gets entity metadata from an entity instance
|
|
81
|
-
* @param entity - Entity instance to get metadata from
|
|
82
|
-
* @returns Entity metadata or undefined if not found
|
|
83
|
-
* @typeParam TTable - Table definition type
|
|
84
|
-
*/
|
|
85
|
-
export const getEntityMeta = <TTable extends TableDef>(entity: unknown): EntityMeta<TTable> | undefined => {
|
|
86
|
-
if (!entity || typeof entity !== 'object') return undefined;
|
|
87
|
-
return (entity as { [ENTITY_META]: EntityMeta<TTable> })[ENTITY_META];
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Checks if an entity has metadata attached
|
|
92
|
-
* @param entity - Entity instance to check
|
|
93
|
-
* @returns True if the entity has metadata, false otherwise
|
|
94
|
-
*/
|
|
95
|
-
export const hasEntityMeta = (entity: unknown): entity is { [ENTITY_META]: EntityMeta<TableDef> } => {
|
|
96
|
-
return Boolean(getEntityMeta(entity));
|
|
97
|
-
};
|
|
1
|
+
import { TableDef } from '../schema/table.js';
|
|
2
|
+
import { RelationIncludeOptions } from '../query-builder/relation-types.js';
|
|
3
|
+
import { EntityContext } from './entity-context.js';
|
|
4
|
+
import { RelationMap } from '../schema/types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Symbol used to store entity metadata on entity instances
|
|
8
|
+
*/
|
|
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> {
|
|
20
|
+
/** Entity context */
|
|
21
|
+
ctx: EntityContext;
|
|
22
|
+
/** Table definition */
|
|
23
|
+
table: TTable;
|
|
24
|
+
/** Relations that should be loaded lazily */
|
|
25
|
+
lazyRelations: RelationKey<TTable>[];
|
|
26
|
+
/** Include options for lazy relations */
|
|
27
|
+
lazyRelationOptions: Map<string, RelationIncludeOptions>;
|
|
28
|
+
/** Cache for relation promises */
|
|
29
|
+
relationCache: Map<string, Promise<unknown>>;
|
|
30
|
+
/** Hydration data for relations */
|
|
31
|
+
relationHydration: Map<string, Map<string, unknown>>;
|
|
32
|
+
/** Relation wrapper instances */
|
|
33
|
+
relationWrappers: Map<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Gets hydration rows for a specific relation and key
|
|
38
|
+
* @param meta - Entity metadata
|
|
39
|
+
* @param relationName - Name of the relation
|
|
40
|
+
* @param key - Key to look up in the hydration map
|
|
41
|
+
* @returns Array of hydration rows or undefined if not found
|
|
42
|
+
* @typeParam TTable - Table definition type
|
|
43
|
+
*/
|
|
44
|
+
export const getHydrationRows = <TTable extends TableDef>(
|
|
45
|
+
meta: EntityMeta<TTable>,
|
|
46
|
+
relationName: string,
|
|
47
|
+
key: unknown
|
|
48
|
+
): Record<string, unknown>[] | undefined => {
|
|
49
|
+
const map = meta.relationHydration.get(relationName);
|
|
50
|
+
if (!map) return undefined;
|
|
51
|
+
const rows = map.get(toKey(key));
|
|
52
|
+
if (!rows) return undefined;
|
|
53
|
+
return Array.isArray(rows) ? rows : undefined;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Gets a single hydration record for a specific relation and key
|
|
58
|
+
* @param meta - Entity metadata
|
|
59
|
+
* @param relationName - Name of the relation
|
|
60
|
+
* @param key - Key to look up in the hydration map
|
|
61
|
+
* @returns Single hydration record or undefined if not found
|
|
62
|
+
* @typeParam TTable - Table definition type
|
|
63
|
+
*/
|
|
64
|
+
export const getHydrationRecord = <TTable extends TableDef>(
|
|
65
|
+
meta: EntityMeta<TTable>,
|
|
66
|
+
relationName: string,
|
|
67
|
+
key: unknown
|
|
68
|
+
): Record<string, unknown> | undefined => {
|
|
69
|
+
const map = meta.relationHydration.get(relationName);
|
|
70
|
+
if (!map) return undefined;
|
|
71
|
+
const value = map.get(toKey(key));
|
|
72
|
+
if (!value) return undefined;
|
|
73
|
+
if (Array.isArray(value)) {
|
|
74
|
+
return value[0];
|
|
75
|
+
}
|
|
76
|
+
return value as Record<string, unknown>;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Gets entity metadata from an entity instance
|
|
81
|
+
* @param entity - Entity instance to get metadata from
|
|
82
|
+
* @returns Entity metadata or undefined if not found
|
|
83
|
+
* @typeParam TTable - Table definition type
|
|
84
|
+
*/
|
|
85
|
+
export const getEntityMeta = <TTable extends TableDef>(entity: unknown): EntityMeta<TTable> | undefined => {
|
|
86
|
+
if (!entity || typeof entity !== 'object') return undefined;
|
|
87
|
+
return (entity as { [ENTITY_META]: EntityMeta<TTable> })[ENTITY_META];
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Checks if an entity has metadata attached
|
|
92
|
+
* @param entity - Entity instance to check
|
|
93
|
+
* @returns True if the entity has metadata, false otherwise
|
|
94
|
+
*/
|
|
95
|
+
export const hasEntityMeta = (entity: unknown): entity is { [ENTITY_META]: EntityMeta<TableDef> } => {
|
|
96
|
+
return Boolean(getEntityMeta(entity));
|
|
97
|
+
};
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import { TableDef } from '../schema/table.js';
|
|
2
|
-
import { EntityConstructor, getAllEntityMetadata } from './entity-metadata.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Reverse lookup registry: TableDef → EntityConstructor
|
|
6
|
-
*/
|
|
7
|
-
const tableToConstructor = new Map<TableDef, EntityConstructor>();
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Gets the entity constructor for a given table definition.
|
|
11
|
-
* @param table - The table definition
|
|
12
|
-
* @returns The entity constructor or undefined if not found
|
|
13
|
-
*/
|
|
14
|
-
export const getConstructorForTable = (table: TableDef): EntityConstructor | undefined => {
|
|
15
|
-
if (!tableToConstructor.size) {
|
|
16
|
-
rebuildRegistry();
|
|
17
|
-
}
|
|
18
|
-
return tableToConstructor.get(table);
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Rebuilds the registry from entity metadata.
|
|
23
|
-
* Called automatically on first lookup or when metadata changes.
|
|
24
|
-
*/
|
|
25
|
-
export const rebuildRegistry = (): void => {
|
|
26
|
-
tableToConstructor.clear();
|
|
27
|
-
for (const meta of getAllEntityMetadata()) {
|
|
28
|
-
if (meta.table) {
|
|
29
|
-
tableToConstructor.set(meta.table, meta.target);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Clears the entity registry.
|
|
36
|
-
*/
|
|
37
|
-
export const clearEntityRegistry = (): void => {
|
|
38
|
-
tableToConstructor.clear();
|
|
39
|
-
};
|
|
1
|
+
import { TableDef } from '../schema/table.js';
|
|
2
|
+
import { EntityConstructor, getAllEntityMetadata } from './entity-metadata.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Reverse lookup registry: TableDef → EntityConstructor
|
|
6
|
+
*/
|
|
7
|
+
const tableToConstructor = new Map<TableDef, EntityConstructor>();
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Gets the entity constructor for a given table definition.
|
|
11
|
+
* @param table - The table definition
|
|
12
|
+
* @returns The entity constructor or undefined if not found
|
|
13
|
+
*/
|
|
14
|
+
export const getConstructorForTable = (table: TableDef): EntityConstructor | undefined => {
|
|
15
|
+
if (!tableToConstructor.size) {
|
|
16
|
+
rebuildRegistry();
|
|
17
|
+
}
|
|
18
|
+
return tableToConstructor.get(table);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Rebuilds the registry from entity metadata.
|
|
23
|
+
* Called automatically on first lookup or when metadata changes.
|
|
24
|
+
*/
|
|
25
|
+
export const rebuildRegistry = (): void => {
|
|
26
|
+
tableToConstructor.clear();
|
|
27
|
+
for (const meta of getAllEntityMetadata()) {
|
|
28
|
+
if (meta.table) {
|
|
29
|
+
tableToConstructor.set(meta.table, meta.target);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Clears the entity registry.
|
|
36
|
+
*/
|
|
37
|
+
export const clearEntityRegistry = (): void => {
|
|
38
|
+
tableToConstructor.clear();
|
|
39
|
+
};
|