metal-orm 1.0.60 → 1.0.62
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 +15 -11
- package/dist/index.cjs +213 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +126 -12
- package/dist/index.d.ts +126 -12
- package/dist/index.js +209 -66
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/decorators/bootstrap.ts +127 -119
- package/src/decorators/index.ts +4 -0
- package/src/orm/entity-materializer.ts +159 -0
- package/src/orm/entity-registry.ts +39 -0
- package/src/orm/execute.ts +62 -18
- package/src/query-builder/select/select-operations.ts +1 -2
- package/src/query-builder/select.ts +85 -15
|
@@ -35,7 +35,9 @@ import { EntityInstance, RelationMap } from '../schema/types.js';
|
|
|
35
35
|
import { OrmSession } from '../orm/orm-session.ts';
|
|
36
36
|
import { ExecutionContext } from '../orm/execution-context.js';
|
|
37
37
|
import { HydrationContext } from '../orm/hydration-context.js';
|
|
38
|
-
import { executeHydrated, executeHydratedWithContexts } from '../orm/execute.js';
|
|
38
|
+
import { executeHydrated, executeHydratedPlain, executeHydratedWithContexts } from '../orm/execute.js';
|
|
39
|
+
import { EntityConstructor } from '../orm/entity-metadata.js';
|
|
40
|
+
import { materializeAs } from '../orm/entity-materializer.js';
|
|
39
41
|
import { resolveSelectQuery } from './query-resolution.js';
|
|
40
42
|
import {
|
|
41
43
|
applyOrderBy,
|
|
@@ -71,7 +73,7 @@ type DeepSelectConfig<TTable extends TableDef> = DeepSelectEntry<TTable>[];
|
|
|
71
73
|
* @typeParam T - Result type for projections (unused)
|
|
72
74
|
* @typeParam TTable - Table definition being queried
|
|
73
75
|
*/
|
|
74
|
-
export class SelectQueryBuilder<T =
|
|
76
|
+
export class SelectQueryBuilder<T = EntityInstance<TableDef>, TTable extends TableDef = TableDef> {
|
|
75
77
|
private readonly env: SelectQueryBuilderEnvironment;
|
|
76
78
|
private readonly context: SelectQueryBuilderContext;
|
|
77
79
|
private readonly columnSelector: ColumnSelector;
|
|
@@ -84,6 +86,7 @@ export class SelectQueryBuilder<T = unknown, TTable extends TableDef = TableDef>
|
|
|
84
86
|
private readonly relationFacet: SelectRelationFacet;
|
|
85
87
|
private readonly lazyRelations: Set<string>;
|
|
86
88
|
private readonly lazyRelationOptions: Map<string, RelationIncludeOptions>;
|
|
89
|
+
private readonly entityConstructor?: EntityConstructor;
|
|
87
90
|
|
|
88
91
|
/**
|
|
89
92
|
* Creates a new SelectQueryBuilder instance
|
|
@@ -98,7 +101,8 @@ export class SelectQueryBuilder<T = unknown, TTable extends TableDef = TableDef>
|
|
|
98
101
|
hydration?: HydrationManager,
|
|
99
102
|
dependencies?: Partial<SelectQueryBuilderDependencies>,
|
|
100
103
|
lazyRelations?: Set<string>,
|
|
101
|
-
lazyRelationOptions?: Map<string, RelationIncludeOptions
|
|
104
|
+
lazyRelationOptions?: Map<string, RelationIncludeOptions>,
|
|
105
|
+
entityConstructor?: EntityConstructor
|
|
102
106
|
) {
|
|
103
107
|
const deps = resolveSelectQueryBuilderDependencies(dependencies);
|
|
104
108
|
this.env = { table, deps };
|
|
@@ -111,6 +115,7 @@ export class SelectQueryBuilder<T = unknown, TTable extends TableDef = TableDef>
|
|
|
111
115
|
};
|
|
112
116
|
this.lazyRelations = new Set(lazyRelations ?? []);
|
|
113
117
|
this.lazyRelationOptions = new Map(lazyRelationOptions ?? []);
|
|
118
|
+
this.entityConstructor = entityConstructor;
|
|
114
119
|
this.columnSelector = deps.createColumnSelector(this.env);
|
|
115
120
|
const relationManager = deps.createRelationManager(this.env);
|
|
116
121
|
this.fromFacet = new SelectFromFacet(this.env, createAstService);
|
|
@@ -139,7 +144,8 @@ export class SelectQueryBuilder<T = unknown, TTable extends TableDef = TableDef>
|
|
|
139
144
|
context.hydration,
|
|
140
145
|
this.env.deps,
|
|
141
146
|
lazyRelations,
|
|
142
|
-
lazyRelationOptions
|
|
147
|
+
lazyRelationOptions,
|
|
148
|
+
this.entityConstructor
|
|
143
149
|
);
|
|
144
150
|
}
|
|
145
151
|
|
|
@@ -625,16 +631,74 @@ export class SelectQueryBuilder<T = unknown, TTable extends TableDef = TableDef>
|
|
|
625
631
|
}
|
|
626
632
|
|
|
627
633
|
/**
|
|
628
|
-
*
|
|
634
|
+
* Ensures that if no columns are selected, all columns from the table are selected by default.
|
|
635
|
+
*/
|
|
636
|
+
private ensureDefaultSelection(): SelectQueryBuilder<T, TTable> {
|
|
637
|
+
const columns = this.context.state.ast.columns;
|
|
638
|
+
if (!columns || columns.length === 0) {
|
|
639
|
+
const columnKeys = Object.keys(this.env.table.columns) as (keyof TTable['columns'] & string)[];
|
|
640
|
+
return this.select(...columnKeys);
|
|
641
|
+
}
|
|
642
|
+
return this;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Executes the query and returns hydrated results.
|
|
647
|
+
* If the builder was created with an entity constructor (e.g. via selectFromEntity),
|
|
648
|
+
* this will automatically return fully materialized entity instances.
|
|
649
|
+
*
|
|
629
650
|
* @param ctx - ORM session context
|
|
630
|
-
* @returns Promise of entity instances
|
|
651
|
+
* @returns Promise of entity instances (or objects if generic T is not an entity)
|
|
631
652
|
* @example
|
|
632
|
-
* const users = await
|
|
633
|
-
*
|
|
634
|
-
*
|
|
653
|
+
* const users = await selectFromEntity(User).execute(session);
|
|
654
|
+
* // users is User[]
|
|
655
|
+
* users[0] instanceof User; // true
|
|
656
|
+
*/
|
|
657
|
+
async execute(ctx: OrmSession): Promise<T[]> {
|
|
658
|
+
if (this.entityConstructor) {
|
|
659
|
+
return this.executeAs(this.entityConstructor, ctx) as unknown as T[];
|
|
660
|
+
}
|
|
661
|
+
const builder = this.ensureDefaultSelection();
|
|
662
|
+
return executeHydrated(ctx, builder) as unknown as T[];
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Executes the query and returns plain row objects (POJOs), ignoring any entity materialization.
|
|
667
|
+
* Use this if you want raw data even when using selectFromEntity.
|
|
668
|
+
*
|
|
669
|
+
* @param ctx - ORM session context
|
|
670
|
+
* @returns Promise of plain entity instances
|
|
671
|
+
* @example
|
|
672
|
+
* const rows = await selectFromEntity(User).executePlain(session);
|
|
673
|
+
* // rows is EntityInstance<UserTable>[] (plain objects)
|
|
674
|
+
* rows[0] instanceof User; // false
|
|
675
|
+
*/
|
|
676
|
+
async executePlain(ctx: OrmSession): Promise<EntityInstance<TTable>[]> {
|
|
677
|
+
const builder = this.ensureDefaultSelection();
|
|
678
|
+
return executeHydratedPlain(ctx, builder) as EntityInstance<TTable>[];
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Executes the query and returns results as real class instances.
|
|
683
|
+
* Unlike execute(), this returns actual instances of the decorated entity class
|
|
684
|
+
* with working methods and proper instanceof checks.
|
|
685
|
+
* @param entityClass - The entity class constructor
|
|
686
|
+
* @param ctx - ORM session context
|
|
687
|
+
* @returns Promise of entity class instances
|
|
688
|
+
* @example
|
|
689
|
+
* const users = await selectFromEntity(User)
|
|
690
|
+
* .include('posts')
|
|
691
|
+
* .executeAs(User, session);
|
|
692
|
+
* users[0] instanceof User; // true!
|
|
693
|
+
* users[0].getFullName(); // works!
|
|
635
694
|
*/
|
|
636
|
-
async
|
|
637
|
-
|
|
695
|
+
async executeAs<TEntity extends object>(
|
|
696
|
+
entityClass: EntityConstructor<TEntity>,
|
|
697
|
+
ctx: OrmSession
|
|
698
|
+
): Promise<TEntity[]> {
|
|
699
|
+
const builder = this.ensureDefaultSelection();
|
|
700
|
+
const results = await executeHydrated(ctx, builder);
|
|
701
|
+
return materializeAs(entityClass, results as unknown as Record<string, unknown>[]);
|
|
638
702
|
}
|
|
639
703
|
|
|
640
704
|
/**
|
|
@@ -656,8 +720,9 @@ export class SelectQueryBuilder<T = unknown, TTable extends TableDef = TableDef>
|
|
|
656
720
|
async executePaged(
|
|
657
721
|
session: OrmSession,
|
|
658
722
|
options: { page: number; pageSize: number }
|
|
659
|
-
): Promise<{ items:
|
|
660
|
-
|
|
723
|
+
): Promise<{ items: T[]; totalItems: number }> {
|
|
724
|
+
const builder = this.ensureDefaultSelection();
|
|
725
|
+
return executePagedQuery(builder, session, options, sess => this.count(sess));
|
|
661
726
|
}
|
|
662
727
|
|
|
663
728
|
/**
|
|
@@ -670,8 +735,13 @@ export class SelectQueryBuilder<T = unknown, TTable extends TableDef = TableDef>
|
|
|
670
735
|
* const hydCtx = new HydrationContext();
|
|
671
736
|
* const users = await qb.executeWithContexts(execCtx, hydCtx);
|
|
672
737
|
*/
|
|
673
|
-
async executeWithContexts(execCtx: ExecutionContext, hydCtx: HydrationContext): Promise<
|
|
674
|
-
|
|
738
|
+
async executeWithContexts(execCtx: ExecutionContext, hydCtx: HydrationContext): Promise<T[]> {
|
|
739
|
+
const builder = this.ensureDefaultSelection();
|
|
740
|
+
const results = await executeHydratedWithContexts(execCtx, hydCtx, builder);
|
|
741
|
+
if (this.entityConstructor) {
|
|
742
|
+
return materializeAs(this.entityConstructor, results as unknown as Record<string, unknown>[]) as unknown as T[];
|
|
743
|
+
}
|
|
744
|
+
return results as unknown as T[];
|
|
675
745
|
}
|
|
676
746
|
|
|
677
747
|
/**
|