metal-orm 1.0.60 → 1.0.63
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 +266 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +163 -16
- package/dist/index.d.ts +163 -16
- package/dist/index.js +259 -73
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/ast/expression.ts +9 -0
- package/src/decorators/bootstrap.ts +149 -137
- 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/hydration-planner.ts +14 -16
- package/src/query-builder/select/select-operations.ts +1 -2
- package/src/query-builder/select.ts +173 -67
package/README.md
CHANGED
|
@@ -149,7 +149,7 @@ If you like explicit model classes, you can add a thin decorator layer on top of
|
|
|
149
149
|
- `@BelongsTo({ target, foreignKey, ... })`
|
|
150
150
|
- `@BelongsToMany({ target, pivotTable, ... })`
|
|
151
151
|
- `bootstrapEntities()` scans metadata, builds `TableDef`s, wires relations with the same `hasOne` / `hasMany` / `belongsTo` / `belongsToMany` helpers you would use manually, and returns the resulting tables. (If you forget to call it, `getTableDefFromEntity` / `selectFromEntity` will bootstrap lazily on first use, but bootstrapping once at startup lets you reuse the same table defs and generate schema SQL.)
|
|
152
|
-
- `selectFromEntity(MyEntity)` lets you start a `SelectQueryBuilder` directly from the class.
|
|
152
|
+
- `selectFromEntity(MyEntity)` lets you start a `SelectQueryBuilder` directly from the class. By default, `execute(session)` returns actual entity instances with all columns selected.
|
|
153
153
|
- **Generate entities from an existing DB**: `npx metal-orm-gen -- --dialect=postgres --url=$DATABASE_URL --schema=public --out=src/entities.ts` introspects your schema and spits out `@Entity` / `@Column` classes you can immediately `bootstrapEntities()` with.
|
|
154
154
|
|
|
155
155
|
You don’t have to use decorators, but when you do, you’re still on the same AST + dialect + runtime foundation.
|
|
@@ -465,14 +465,14 @@ What the runtime gives you:
|
|
|
465
465
|
<a id="level-3"></a>
|
|
466
466
|
### Level 3: Decorator entities ✨
|
|
467
467
|
|
|
468
|
-
Finally, you can describe your models with decorators and still use the same runtime and query builder.
|
|
469
|
-
|
|
470
|
-
The decorator layer is built on the TC39 Stage 3 standard (TypeScript 5.6+), so you simply decorate class fields (or accessors if you need custom logic) and the standard `ClassFieldDecoratorContext` keeps a metadata bag on `context.metadata`/`Symbol.metadata`. `@Entity` reads that bag when it runs and builds your `TableDef`s—no `experimentalDecorators`, parameter decorators, or extra polyfills required.
|
|
471
|
-
|
|
472
|
-
```ts
|
|
473
|
-
import mysql from 'mysql2/promise';
|
|
474
|
-
import {
|
|
475
|
-
Orm,
|
|
468
|
+
Finally, you can describe your models with decorators and still use the same runtime and query builder.
|
|
469
|
+
|
|
470
|
+
The decorator layer is built on the TC39 Stage 3 standard (TypeScript 5.6+), so you simply decorate class fields (or accessors if you need custom logic) and the standard `ClassFieldDecoratorContext` keeps a metadata bag on `context.metadata`/`Symbol.metadata`. `@Entity` reads that bag when it runs and builds your `TableDef`s—no `experimentalDecorators`, parameter decorators, or extra polyfills required.
|
|
471
|
+
|
|
472
|
+
```ts
|
|
473
|
+
import mysql from 'mysql2/promise';
|
|
474
|
+
import {
|
|
475
|
+
Orm,
|
|
476
476
|
OrmSession,
|
|
477
477
|
MySqlDialect,
|
|
478
478
|
col,
|
|
@@ -547,13 +547,17 @@ const [user] = await selectFromEntity(User)
|
|
|
547
547
|
.select('id', 'name')
|
|
548
548
|
.includeLazy('posts')
|
|
549
549
|
.where(eq(U.id, 1))
|
|
550
|
-
.execute(session);
|
|
550
|
+
.execute(session); // user is an actual instance of the User class!
|
|
551
|
+
|
|
552
|
+
// Use executePlain() if you want raw POJOs instead of class instances
|
|
553
|
+
const [rawUser] = await selectFromEntity(User).executePlain(session);
|
|
551
554
|
|
|
552
555
|
user.posts.add({ title: 'From decorators' });
|
|
553
556
|
await session.commit();
|
|
554
557
|
```
|
|
555
558
|
|
|
556
|
-
Tip: to keep selections terse, use `select`, `include` (with `columns`), or the `sel`/`esel` helpers instead of spelling `table.columns.*` over and over.
|
|
559
|
+
Tip: to keep selections terse, use `select`, `include` (with `columns`), or the `sel`/`esel` helpers instead of spelling `table.columns.*` over and over. By default, `selectFromEntity` selects all columns if you don't specify any.
|
|
560
|
+
|
|
557
561
|
|
|
558
562
|
This level is nice when:
|
|
559
563
|
|
package/dist/index.cjs
CHANGED
|
@@ -47,7 +47,9 @@ __export(index_exports, {
|
|
|
47
47
|
BelongsTo: () => BelongsTo,
|
|
48
48
|
BelongsToMany: () => BelongsToMany,
|
|
49
49
|
Column: () => Column,
|
|
50
|
+
ConstructorMaterializationStrategy: () => ConstructorMaterializationStrategy,
|
|
50
51
|
DefaultBelongsToReference: () => DefaultBelongsToReference,
|
|
52
|
+
DefaultEntityMaterializer: () => DefaultEntityMaterializer,
|
|
51
53
|
DefaultHasManyCollection: () => DefaultHasManyCollection,
|
|
52
54
|
DefaultManyToManyCollection: () => DefaultManyToManyCollection,
|
|
53
55
|
DeleteQueryBuilder: () => DeleteQueryBuilder,
|
|
@@ -63,6 +65,7 @@ __export(index_exports, {
|
|
|
63
65
|
Pool: () => Pool,
|
|
64
66
|
PostgresDialect: () => PostgresDialect,
|
|
65
67
|
PrimaryKey: () => PrimaryKey,
|
|
68
|
+
PrototypeMaterializationStrategy: () => PrototypeMaterializationStrategy,
|
|
66
69
|
RelationKinds: () => RelationKinds,
|
|
67
70
|
STANDARD_COLUMN_TYPES: () => STANDARD_COLUMN_TYPES,
|
|
68
71
|
SelectQueryBuilder: () => SelectQueryBuilder,
|
|
@@ -78,6 +81,7 @@ __export(index_exports, {
|
|
|
78
81
|
aliasRef: () => aliasRef,
|
|
79
82
|
and: () => and,
|
|
80
83
|
arrayAppend: () => arrayAppend,
|
|
84
|
+
asType: () => asType,
|
|
81
85
|
ascii: () => ascii,
|
|
82
86
|
asin: () => asin,
|
|
83
87
|
atan: () => atan,
|
|
@@ -143,6 +147,8 @@ __export(index_exports, {
|
|
|
143
147
|
eq: () => eq,
|
|
144
148
|
esel: () => esel,
|
|
145
149
|
executeHydrated: () => executeHydrated,
|
|
150
|
+
executeHydratedPlain: () => executeHydratedPlain,
|
|
151
|
+
executeHydratedPlainWithContexts: () => executeHydratedPlainWithContexts,
|
|
146
152
|
executeHydratedWithContexts: () => executeHydratedWithContexts,
|
|
147
153
|
executeSchemaSql: () => executeSchemaSql,
|
|
148
154
|
executeSchemaSqlFor: () => executeSchemaSqlFor,
|
|
@@ -214,6 +220,7 @@ __export(index_exports, {
|
|
|
214
220
|
lt: () => lt,
|
|
215
221
|
lte: () => lte,
|
|
216
222
|
ltrim: () => ltrim,
|
|
223
|
+
materializeAs: () => materializeAs,
|
|
217
224
|
max: () => max,
|
|
218
225
|
md5: () => md5,
|
|
219
226
|
min: () => min,
|
|
@@ -1099,6 +1106,9 @@ var toTableRef = (table) => ({
|
|
|
1099
1106
|
alias: hasAlias(table) ? table.alias : void 0
|
|
1100
1107
|
});
|
|
1101
1108
|
|
|
1109
|
+
// src/core/ast/expression.ts
|
|
1110
|
+
var asType = (expr) => expr;
|
|
1111
|
+
|
|
1102
1112
|
// src/core/functions/function-registry.ts
|
|
1103
1113
|
var FunctionRegistry = class {
|
|
1104
1114
|
renderers = /* @__PURE__ */ new Map();
|
|
@@ -3712,10 +3722,9 @@ var HydrationPlanner = class _HydrationPlanner {
|
|
|
3712
3722
|
const rootCols = new Set(currentPlan.rootColumns);
|
|
3713
3723
|
let changed = false;
|
|
3714
3724
|
columns.forEach((node) => {
|
|
3715
|
-
|
|
3716
|
-
if (
|
|
3717
|
-
|
|
3718
|
-
if (isRelationAlias(alias)) return;
|
|
3725
|
+
const alias = node.type === "Column" ? node.alias || node.name : node.alias;
|
|
3726
|
+
if (!alias || isRelationAlias(alias)) return;
|
|
3727
|
+
if (node.type === "Column" && node.table !== this.table.name) return;
|
|
3719
3728
|
if (!rootCols.has(alias)) {
|
|
3720
3729
|
rootCols.add(alias);
|
|
3721
3730
|
changed = true;
|
|
@@ -6181,9 +6190,22 @@ var executeWithContexts = async (execCtx, entityCtx, qb) => {
|
|
|
6181
6190
|
await loadLazyRelationsForTable(entityCtx, qb.getTable(), lazyRelations, lazyRelationOptions);
|
|
6182
6191
|
return entities;
|
|
6183
6192
|
};
|
|
6193
|
+
var executePlainWithContexts = async (execCtx, qb) => {
|
|
6194
|
+
const ast = qb.getAST();
|
|
6195
|
+
const compiled = execCtx.dialect.compileSelect(ast);
|
|
6196
|
+
const executed = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
|
|
6197
|
+
const rows = flattenResults(executed);
|
|
6198
|
+
if (ast.setOps && ast.setOps.length > 0) {
|
|
6199
|
+
return rows;
|
|
6200
|
+
}
|
|
6201
|
+
return hydrateRows(rows, qb.getHydrationPlan());
|
|
6202
|
+
};
|
|
6184
6203
|
async function executeHydrated(session, qb) {
|
|
6185
6204
|
return executeWithContexts(session.getExecutionContext(), session, qb);
|
|
6186
6205
|
}
|
|
6206
|
+
async function executeHydratedPlain(session, qb) {
|
|
6207
|
+
return executePlainWithContexts(session.getExecutionContext(), qb);
|
|
6208
|
+
}
|
|
6187
6209
|
async function executeHydratedWithContexts(execCtx, hydCtx, qb) {
|
|
6188
6210
|
const entityCtx = hydCtx.entityContext;
|
|
6189
6211
|
if (!entityCtx) {
|
|
@@ -6191,6 +6213,9 @@ async function executeHydratedWithContexts(execCtx, hydCtx, qb) {
|
|
|
6191
6213
|
}
|
|
6192
6214
|
return executeWithContexts(execCtx, entityCtx, qb);
|
|
6193
6215
|
}
|
|
6216
|
+
async function executeHydratedPlainWithContexts(execCtx, qb) {
|
|
6217
|
+
return executePlainWithContexts(execCtx, qb);
|
|
6218
|
+
}
|
|
6194
6219
|
var loadLazyRelationsForTable = async (ctx, table, lazyRelations, lazyRelationOptions) => {
|
|
6195
6220
|
if (!lazyRelations.length) return;
|
|
6196
6221
|
const tracked = ctx.getEntitiesForTable(table);
|
|
@@ -6238,6 +6263,146 @@ var loadLazyRelationsForTable = async (ctx, table, lazyRelations, lazyRelationOp
|
|
|
6238
6263
|
}
|
|
6239
6264
|
};
|
|
6240
6265
|
|
|
6266
|
+
// src/orm/entity-metadata.ts
|
|
6267
|
+
var metadataMap = /* @__PURE__ */ new Map();
|
|
6268
|
+
var ensureEntityMetadata = (target) => {
|
|
6269
|
+
let meta = metadataMap.get(target);
|
|
6270
|
+
if (!meta) {
|
|
6271
|
+
meta = {
|
|
6272
|
+
target,
|
|
6273
|
+
tableName: target.name || "unknown",
|
|
6274
|
+
columns: {},
|
|
6275
|
+
relations: {}
|
|
6276
|
+
};
|
|
6277
|
+
metadataMap.set(target, meta);
|
|
6278
|
+
}
|
|
6279
|
+
return meta;
|
|
6280
|
+
};
|
|
6281
|
+
var getEntityMetadata = (target) => {
|
|
6282
|
+
return metadataMap.get(target);
|
|
6283
|
+
};
|
|
6284
|
+
var getAllEntityMetadata = () => {
|
|
6285
|
+
return Array.from(metadataMap.values());
|
|
6286
|
+
};
|
|
6287
|
+
var addColumnMetadata = (target, propertyKey, column) => {
|
|
6288
|
+
const meta = ensureEntityMetadata(target);
|
|
6289
|
+
meta.columns[propertyKey] = { ...column };
|
|
6290
|
+
};
|
|
6291
|
+
var addRelationMetadata = (target, propertyKey, relation) => {
|
|
6292
|
+
const meta = ensureEntityMetadata(target);
|
|
6293
|
+
meta.relations[propertyKey] = relation;
|
|
6294
|
+
};
|
|
6295
|
+
var setEntityTableName = (target, tableName, hooks) => {
|
|
6296
|
+
const meta = ensureEntityMetadata(target);
|
|
6297
|
+
if (tableName && tableName.length > 0) {
|
|
6298
|
+
meta.tableName = tableName;
|
|
6299
|
+
}
|
|
6300
|
+
if (hooks) {
|
|
6301
|
+
meta.hooks = hooks;
|
|
6302
|
+
}
|
|
6303
|
+
};
|
|
6304
|
+
var buildTableDef = (meta) => {
|
|
6305
|
+
if (meta.table) {
|
|
6306
|
+
return meta.table;
|
|
6307
|
+
}
|
|
6308
|
+
const columns = {};
|
|
6309
|
+
for (const [key, def] of Object.entries(meta.columns)) {
|
|
6310
|
+
columns[key] = {
|
|
6311
|
+
...def,
|
|
6312
|
+
name: key,
|
|
6313
|
+
table: meta.tableName
|
|
6314
|
+
};
|
|
6315
|
+
}
|
|
6316
|
+
const table = defineTable(meta.tableName, columns, {}, meta.hooks);
|
|
6317
|
+
meta.table = table;
|
|
6318
|
+
return table;
|
|
6319
|
+
};
|
|
6320
|
+
|
|
6321
|
+
// src/orm/entity-registry.ts
|
|
6322
|
+
var tableToConstructor = /* @__PURE__ */ new Map();
|
|
6323
|
+
var rebuildRegistry = () => {
|
|
6324
|
+
tableToConstructor.clear();
|
|
6325
|
+
for (const meta of getAllEntityMetadata()) {
|
|
6326
|
+
if (meta.table) {
|
|
6327
|
+
tableToConstructor.set(meta.table, meta.target);
|
|
6328
|
+
}
|
|
6329
|
+
}
|
|
6330
|
+
};
|
|
6331
|
+
|
|
6332
|
+
// src/orm/entity-materializer.ts
|
|
6333
|
+
var PrototypeMaterializationStrategy = class {
|
|
6334
|
+
materialize(ctor, data) {
|
|
6335
|
+
const instance = Object.create(ctor.prototype);
|
|
6336
|
+
Object.assign(instance, data);
|
|
6337
|
+
return instance;
|
|
6338
|
+
}
|
|
6339
|
+
};
|
|
6340
|
+
var ConstructorMaterializationStrategy = class {
|
|
6341
|
+
materialize(ctor, data) {
|
|
6342
|
+
const instance = Reflect.construct(ctor, []);
|
|
6343
|
+
Object.assign(instance, data);
|
|
6344
|
+
return instance;
|
|
6345
|
+
}
|
|
6346
|
+
};
|
|
6347
|
+
var DefaultEntityMaterializer = class {
|
|
6348
|
+
constructor(strategy = new ConstructorMaterializationStrategy()) {
|
|
6349
|
+
this.strategy = strategy;
|
|
6350
|
+
}
|
|
6351
|
+
materialize(ctor, row) {
|
|
6352
|
+
if (hasEntityMeta(row)) {
|
|
6353
|
+
return this.materializeEntityProxy(ctor, row);
|
|
6354
|
+
}
|
|
6355
|
+
const instance = this.strategy.materialize(ctor, row);
|
|
6356
|
+
this.materializeRelations(instance);
|
|
6357
|
+
return instance;
|
|
6358
|
+
}
|
|
6359
|
+
materializeMany(ctor, rows) {
|
|
6360
|
+
return rows.map((row) => this.materialize(ctor, row));
|
|
6361
|
+
}
|
|
6362
|
+
/**
|
|
6363
|
+
* Recursively materializes nested relation data.
|
|
6364
|
+
*/
|
|
6365
|
+
materializeRelations(instance) {
|
|
6366
|
+
rebuildRegistry();
|
|
6367
|
+
for (const value of Object.values(instance)) {
|
|
6368
|
+
if (value === null || value === void 0) continue;
|
|
6369
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
6370
|
+
const nested = value;
|
|
6371
|
+
if (this.isEntityLike(nested)) {
|
|
6372
|
+
}
|
|
6373
|
+
}
|
|
6374
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
6375
|
+
const first = value[0];
|
|
6376
|
+
if (typeof first === "object" && first !== null && this.isEntityLike(first)) {
|
|
6377
|
+
}
|
|
6378
|
+
}
|
|
6379
|
+
}
|
|
6380
|
+
}
|
|
6381
|
+
/**
|
|
6382
|
+
* Simple heuristic to check if an object looks like an entity.
|
|
6383
|
+
*/
|
|
6384
|
+
isEntityLike(obj) {
|
|
6385
|
+
return "id" in obj || Object.keys(obj).some(
|
|
6386
|
+
(k) => k.endsWith("Id") || k === "createdAt" || k === "updatedAt"
|
|
6387
|
+
);
|
|
6388
|
+
}
|
|
6389
|
+
materializeEntityProxy(ctor, row) {
|
|
6390
|
+
const proxy = row;
|
|
6391
|
+
const baseline = this.strategy.materialize(ctor, {});
|
|
6392
|
+
for (const key of Object.keys(baseline)) {
|
|
6393
|
+
if (!Object.prototype.hasOwnProperty.call(proxy, key)) {
|
|
6394
|
+
proxy[key] = baseline[key];
|
|
6395
|
+
}
|
|
6396
|
+
}
|
|
6397
|
+
Object.setPrototypeOf(proxy, ctor.prototype);
|
|
6398
|
+
return proxy;
|
|
6399
|
+
}
|
|
6400
|
+
};
|
|
6401
|
+
var materializeAs = (ctor, results) => {
|
|
6402
|
+
const materializer = new DefaultEntityMaterializer();
|
|
6403
|
+
return materializer.materializeMany(ctor, results);
|
|
6404
|
+
};
|
|
6405
|
+
|
|
6241
6406
|
// src/query-builder/query-resolution.ts
|
|
6242
6407
|
function resolveSelectQuery(query) {
|
|
6243
6408
|
const candidate = query;
|
|
@@ -6648,6 +6813,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6648
6813
|
relationFacet;
|
|
6649
6814
|
lazyRelations;
|
|
6650
6815
|
lazyRelationOptions;
|
|
6816
|
+
entityConstructor;
|
|
6651
6817
|
/**
|
|
6652
6818
|
* Creates a new SelectQueryBuilder instance
|
|
6653
6819
|
* @param table - Table definition to query
|
|
@@ -6655,7 +6821,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6655
6821
|
* @param hydration - Optional hydration manager
|
|
6656
6822
|
* @param dependencies - Optional query builder dependencies
|
|
6657
6823
|
*/
|
|
6658
|
-
constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions) {
|
|
6824
|
+
constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions, entityConstructor) {
|
|
6659
6825
|
const deps = resolveSelectQueryBuilderDependencies(dependencies);
|
|
6660
6826
|
this.env = { table, deps };
|
|
6661
6827
|
const createAstService = (nextState) => deps.createQueryAstService(table, nextState);
|
|
@@ -6667,6 +6833,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6667
6833
|
};
|
|
6668
6834
|
this.lazyRelations = new Set(lazyRelations ?? []);
|
|
6669
6835
|
this.lazyRelationOptions = new Map(lazyRelationOptions ?? []);
|
|
6836
|
+
this.entityConstructor = entityConstructor;
|
|
6670
6837
|
this.columnSelector = deps.createColumnSelector(this.env);
|
|
6671
6838
|
const relationManager = deps.createRelationManager(this.env);
|
|
6672
6839
|
this.fromFacet = new SelectFromFacet(this.env, createAstService);
|
|
@@ -6690,7 +6857,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6690
6857
|
context.hydration,
|
|
6691
6858
|
this.env.deps,
|
|
6692
6859
|
lazyRelations,
|
|
6693
|
-
lazyRelationOptions
|
|
6860
|
+
lazyRelationOptions,
|
|
6861
|
+
this.entityConstructor
|
|
6694
6862
|
);
|
|
6695
6863
|
}
|
|
6696
6864
|
/**
|
|
@@ -6738,7 +6906,9 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6738
6906
|
select(...args) {
|
|
6739
6907
|
if (args.length === 1 && typeof args[0] === "object" && args[0] !== null && typeof args[0] !== "string") {
|
|
6740
6908
|
const columns = args[0];
|
|
6741
|
-
return this.clone(
|
|
6909
|
+
return this.clone(
|
|
6910
|
+
this.projectionFacet.select(this.context, columns)
|
|
6911
|
+
);
|
|
6742
6912
|
}
|
|
6743
6913
|
const cols = args;
|
|
6744
6914
|
const selection = {};
|
|
@@ -6749,7 +6919,9 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6749
6919
|
}
|
|
6750
6920
|
selection[key] = col2;
|
|
6751
6921
|
}
|
|
6752
|
-
return this.clone(
|
|
6922
|
+
return this.clone(
|
|
6923
|
+
this.projectionFacet.select(this.context, selection)
|
|
6924
|
+
);
|
|
6753
6925
|
}
|
|
6754
6926
|
/**
|
|
6755
6927
|
* Selects raw column expressions
|
|
@@ -6853,7 +7025,9 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6853
7025
|
*/
|
|
6854
7026
|
selectSubquery(alias, sub2) {
|
|
6855
7027
|
const query = resolveSelectQuery(sub2);
|
|
6856
|
-
return this.clone(
|
|
7028
|
+
return this.clone(
|
|
7029
|
+
this.projectionFacet.selectSubquery(this.context, alias, query)
|
|
7030
|
+
);
|
|
6857
7031
|
}
|
|
6858
7032
|
/**
|
|
6859
7033
|
* Adds a JOIN against a derived table (subquery with alias)
|
|
@@ -7081,16 +7255,69 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7081
7255
|
return this.env.table;
|
|
7082
7256
|
}
|
|
7083
7257
|
/**
|
|
7084
|
-
*
|
|
7258
|
+
* Ensures that if no columns are selected, all columns from the table are selected by default.
|
|
7259
|
+
*/
|
|
7260
|
+
ensureDefaultSelection() {
|
|
7261
|
+
const columns = this.context.state.ast.columns;
|
|
7262
|
+
if (!columns || columns.length === 0) {
|
|
7263
|
+
const columnKeys = Object.keys(this.env.table.columns);
|
|
7264
|
+
return this.select(...columnKeys);
|
|
7265
|
+
}
|
|
7266
|
+
return this;
|
|
7267
|
+
}
|
|
7268
|
+
/**
|
|
7269
|
+
* Executes the query and returns hydrated results.
|
|
7270
|
+
* If the builder was created with an entity constructor (e.g. via selectFromEntity),
|
|
7271
|
+
* this will automatically return fully materialized entity instances.
|
|
7272
|
+
*
|
|
7085
7273
|
* @param ctx - ORM session context
|
|
7086
|
-
* @returns Promise of entity instances
|
|
7274
|
+
* @returns Promise of entity instances (or objects if generic T is not an entity)
|
|
7087
7275
|
* @example
|
|
7088
|
-
* const users = await
|
|
7089
|
-
*
|
|
7090
|
-
*
|
|
7276
|
+
* const users = await selectFromEntity(User).execute(session);
|
|
7277
|
+
* // users is User[]
|
|
7278
|
+
* users[0] instanceof User; // true
|
|
7091
7279
|
*/
|
|
7092
7280
|
async execute(ctx) {
|
|
7093
|
-
|
|
7281
|
+
if (this.entityConstructor) {
|
|
7282
|
+
return this.executeAs(this.entityConstructor, ctx);
|
|
7283
|
+
}
|
|
7284
|
+
const builder = this.ensureDefaultSelection();
|
|
7285
|
+
return executeHydrated(ctx, builder);
|
|
7286
|
+
}
|
|
7287
|
+
/**
|
|
7288
|
+
* Executes the query and returns plain row objects (POJOs), ignoring any entity materialization.
|
|
7289
|
+
* Use this if you want raw data even when using selectFromEntity.
|
|
7290
|
+
*
|
|
7291
|
+
* @param ctx - ORM session context
|
|
7292
|
+
* @returns Promise of plain entity instances
|
|
7293
|
+
* @example
|
|
7294
|
+
* const rows = await selectFromEntity(User).executePlain(session);
|
|
7295
|
+
* // rows is EntityInstance<UserTable>[] (plain objects)
|
|
7296
|
+
* rows[0] instanceof User; // false
|
|
7297
|
+
*/
|
|
7298
|
+
async executePlain(ctx) {
|
|
7299
|
+
const builder = this.ensureDefaultSelection();
|
|
7300
|
+
const rows = await executeHydratedPlain(ctx, builder);
|
|
7301
|
+
return rows;
|
|
7302
|
+
}
|
|
7303
|
+
/**
|
|
7304
|
+
* Executes the query and returns results as real class instances.
|
|
7305
|
+
* Unlike execute(), this returns actual instances of the decorated entity class
|
|
7306
|
+
* with working methods and proper instanceof checks.
|
|
7307
|
+
* @param entityClass - The entity class constructor
|
|
7308
|
+
* @param ctx - ORM session context
|
|
7309
|
+
* @returns Promise of entity class instances
|
|
7310
|
+
* @example
|
|
7311
|
+
* const users = await selectFromEntity(User)
|
|
7312
|
+
* .include('posts')
|
|
7313
|
+
* .executeAs(User, session);
|
|
7314
|
+
* users[0] instanceof User; // true!
|
|
7315
|
+
* users[0].getFullName(); // works!
|
|
7316
|
+
*/
|
|
7317
|
+
async executeAs(entityClass, ctx) {
|
|
7318
|
+
const builder = this.ensureDefaultSelection();
|
|
7319
|
+
const results = await executeHydrated(ctx, builder);
|
|
7320
|
+
return materializeAs(entityClass, results);
|
|
7094
7321
|
}
|
|
7095
7322
|
/**
|
|
7096
7323
|
* Executes a count query for the current builder without LIMIT/OFFSET clauses.
|
|
@@ -7108,7 +7335,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7108
7335
|
* const { items, totalItems } = await qb.executePaged(session, { page: 1, pageSize: 20 });
|
|
7109
7336
|
*/
|
|
7110
7337
|
async executePaged(session, options) {
|
|
7111
|
-
|
|
7338
|
+
const builder = this.ensureDefaultSelection();
|
|
7339
|
+
return executePagedQuery(builder, session, options, (sess) => this.count(sess));
|
|
7112
7340
|
}
|
|
7113
7341
|
/**
|
|
7114
7342
|
* Executes the query with provided execution and hydration contexts
|
|
@@ -7121,7 +7349,12 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7121
7349
|
* const users = await qb.executeWithContexts(execCtx, hydCtx);
|
|
7122
7350
|
*/
|
|
7123
7351
|
async executeWithContexts(execCtx, hydCtx) {
|
|
7124
|
-
|
|
7352
|
+
const builder = this.ensureDefaultSelection();
|
|
7353
|
+
const results = await executeHydratedWithContexts(execCtx, hydCtx, builder);
|
|
7354
|
+
if (this.entityConstructor) {
|
|
7355
|
+
return materializeAs(this.entityConstructor, results);
|
|
7356
|
+
}
|
|
7357
|
+
return results;
|
|
7125
7358
|
}
|
|
7126
7359
|
/**
|
|
7127
7360
|
* Adds a WHERE condition to the query
|
|
@@ -7414,61 +7647,6 @@ var isTableDef = (value) => {
|
|
|
7414
7647
|
return true;
|
|
7415
7648
|
};
|
|
7416
7649
|
|
|
7417
|
-
// src/orm/entity-metadata.ts
|
|
7418
|
-
var metadataMap = /* @__PURE__ */ new Map();
|
|
7419
|
-
var ensureEntityMetadata = (target) => {
|
|
7420
|
-
let meta = metadataMap.get(target);
|
|
7421
|
-
if (!meta) {
|
|
7422
|
-
meta = {
|
|
7423
|
-
target,
|
|
7424
|
-
tableName: target.name || "unknown",
|
|
7425
|
-
columns: {},
|
|
7426
|
-
relations: {}
|
|
7427
|
-
};
|
|
7428
|
-
metadataMap.set(target, meta);
|
|
7429
|
-
}
|
|
7430
|
-
return meta;
|
|
7431
|
-
};
|
|
7432
|
-
var getEntityMetadata = (target) => {
|
|
7433
|
-
return metadataMap.get(target);
|
|
7434
|
-
};
|
|
7435
|
-
var getAllEntityMetadata = () => {
|
|
7436
|
-
return Array.from(metadataMap.values());
|
|
7437
|
-
};
|
|
7438
|
-
var addColumnMetadata = (target, propertyKey, column) => {
|
|
7439
|
-
const meta = ensureEntityMetadata(target);
|
|
7440
|
-
meta.columns[propertyKey] = { ...column };
|
|
7441
|
-
};
|
|
7442
|
-
var addRelationMetadata = (target, propertyKey, relation) => {
|
|
7443
|
-
const meta = ensureEntityMetadata(target);
|
|
7444
|
-
meta.relations[propertyKey] = relation;
|
|
7445
|
-
};
|
|
7446
|
-
var setEntityTableName = (target, tableName, hooks) => {
|
|
7447
|
-
const meta = ensureEntityMetadata(target);
|
|
7448
|
-
if (tableName && tableName.length > 0) {
|
|
7449
|
-
meta.tableName = tableName;
|
|
7450
|
-
}
|
|
7451
|
-
if (hooks) {
|
|
7452
|
-
meta.hooks = hooks;
|
|
7453
|
-
}
|
|
7454
|
-
};
|
|
7455
|
-
var buildTableDef = (meta) => {
|
|
7456
|
-
if (meta.table) {
|
|
7457
|
-
return meta.table;
|
|
7458
|
-
}
|
|
7459
|
-
const columns = {};
|
|
7460
|
-
for (const [key, def] of Object.entries(meta.columns)) {
|
|
7461
|
-
columns[key] = {
|
|
7462
|
-
...def,
|
|
7463
|
-
name: key,
|
|
7464
|
-
table: meta.tableName
|
|
7465
|
-
};
|
|
7466
|
-
}
|
|
7467
|
-
const table = defineTable(meta.tableName, columns, {}, meta.hooks);
|
|
7468
|
-
meta.table = table;
|
|
7469
|
-
return table;
|
|
7470
|
-
};
|
|
7471
|
-
|
|
7472
7650
|
// src/decorators/bootstrap.ts
|
|
7473
7651
|
var unwrapTarget = (target) => {
|
|
7474
7652
|
if (typeof target === "function" && target.prototype === void 0) {
|
|
@@ -7588,7 +7766,15 @@ var selectFromEntity = (ctor) => {
|
|
|
7588
7766
|
if (!table) {
|
|
7589
7767
|
throw new Error(`Entity '${ctor.name}' is not registered with decorators or has not been bootstrapped`);
|
|
7590
7768
|
}
|
|
7591
|
-
return new SelectQueryBuilder(
|
|
7769
|
+
return new SelectQueryBuilder(
|
|
7770
|
+
table,
|
|
7771
|
+
void 0,
|
|
7772
|
+
void 0,
|
|
7773
|
+
void 0,
|
|
7774
|
+
void 0,
|
|
7775
|
+
void 0,
|
|
7776
|
+
ctor
|
|
7777
|
+
);
|
|
7592
7778
|
};
|
|
7593
7779
|
var entityRef = (ctor) => {
|
|
7594
7780
|
const table = getTableDefFromEntity(ctor);
|
|
@@ -12834,7 +13020,9 @@ function createPooledExecutorFactory(opts) {
|
|
|
12834
13020
|
BelongsTo,
|
|
12835
13021
|
BelongsToMany,
|
|
12836
13022
|
Column,
|
|
13023
|
+
ConstructorMaterializationStrategy,
|
|
12837
13024
|
DefaultBelongsToReference,
|
|
13025
|
+
DefaultEntityMaterializer,
|
|
12838
13026
|
DefaultHasManyCollection,
|
|
12839
13027
|
DefaultManyToManyCollection,
|
|
12840
13028
|
DeleteQueryBuilder,
|
|
@@ -12850,6 +13038,7 @@ function createPooledExecutorFactory(opts) {
|
|
|
12850
13038
|
Pool,
|
|
12851
13039
|
PostgresDialect,
|
|
12852
13040
|
PrimaryKey,
|
|
13041
|
+
PrototypeMaterializationStrategy,
|
|
12853
13042
|
RelationKinds,
|
|
12854
13043
|
STANDARD_COLUMN_TYPES,
|
|
12855
13044
|
SelectQueryBuilder,
|
|
@@ -12865,6 +13054,7 @@ function createPooledExecutorFactory(opts) {
|
|
|
12865
13054
|
aliasRef,
|
|
12866
13055
|
and,
|
|
12867
13056
|
arrayAppend,
|
|
13057
|
+
asType,
|
|
12868
13058
|
ascii,
|
|
12869
13059
|
asin,
|
|
12870
13060
|
atan,
|
|
@@ -12930,6 +13120,8 @@ function createPooledExecutorFactory(opts) {
|
|
|
12930
13120
|
eq,
|
|
12931
13121
|
esel,
|
|
12932
13122
|
executeHydrated,
|
|
13123
|
+
executeHydratedPlain,
|
|
13124
|
+
executeHydratedPlainWithContexts,
|
|
12933
13125
|
executeHydratedWithContexts,
|
|
12934
13126
|
executeSchemaSql,
|
|
12935
13127
|
executeSchemaSqlFor,
|
|
@@ -13001,6 +13193,7 @@ function createPooledExecutorFactory(opts) {
|
|
|
13001
13193
|
lt,
|
|
13002
13194
|
lte,
|
|
13003
13195
|
ltrim,
|
|
13196
|
+
materializeAs,
|
|
13004
13197
|
max,
|
|
13005
13198
|
md5,
|
|
13006
13199
|
min,
|