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
package/dist/index.js
CHANGED
|
@@ -5970,6 +5970,132 @@ var loadLazyRelationsForTable = async (ctx, table, lazyRelations, lazyRelationOp
|
|
|
5970
5970
|
}
|
|
5971
5971
|
};
|
|
5972
5972
|
|
|
5973
|
+
// src/orm/entity-metadata.ts
|
|
5974
|
+
var metadataMap = /* @__PURE__ */ new Map();
|
|
5975
|
+
var ensureEntityMetadata = (target) => {
|
|
5976
|
+
let meta = metadataMap.get(target);
|
|
5977
|
+
if (!meta) {
|
|
5978
|
+
meta = {
|
|
5979
|
+
target,
|
|
5980
|
+
tableName: target.name || "unknown",
|
|
5981
|
+
columns: {},
|
|
5982
|
+
relations: {}
|
|
5983
|
+
};
|
|
5984
|
+
metadataMap.set(target, meta);
|
|
5985
|
+
}
|
|
5986
|
+
return meta;
|
|
5987
|
+
};
|
|
5988
|
+
var getEntityMetadata = (target) => {
|
|
5989
|
+
return metadataMap.get(target);
|
|
5990
|
+
};
|
|
5991
|
+
var getAllEntityMetadata = () => {
|
|
5992
|
+
return Array.from(metadataMap.values());
|
|
5993
|
+
};
|
|
5994
|
+
var addColumnMetadata = (target, propertyKey, column) => {
|
|
5995
|
+
const meta = ensureEntityMetadata(target);
|
|
5996
|
+
meta.columns[propertyKey] = { ...column };
|
|
5997
|
+
};
|
|
5998
|
+
var addRelationMetadata = (target, propertyKey, relation) => {
|
|
5999
|
+
const meta = ensureEntityMetadata(target);
|
|
6000
|
+
meta.relations[propertyKey] = relation;
|
|
6001
|
+
};
|
|
6002
|
+
var setEntityTableName = (target, tableName, hooks) => {
|
|
6003
|
+
const meta = ensureEntityMetadata(target);
|
|
6004
|
+
if (tableName && tableName.length > 0) {
|
|
6005
|
+
meta.tableName = tableName;
|
|
6006
|
+
}
|
|
6007
|
+
if (hooks) {
|
|
6008
|
+
meta.hooks = hooks;
|
|
6009
|
+
}
|
|
6010
|
+
};
|
|
6011
|
+
var buildTableDef = (meta) => {
|
|
6012
|
+
if (meta.table) {
|
|
6013
|
+
return meta.table;
|
|
6014
|
+
}
|
|
6015
|
+
const columns = {};
|
|
6016
|
+
for (const [key, def] of Object.entries(meta.columns)) {
|
|
6017
|
+
columns[key] = {
|
|
6018
|
+
...def,
|
|
6019
|
+
name: key,
|
|
6020
|
+
table: meta.tableName
|
|
6021
|
+
};
|
|
6022
|
+
}
|
|
6023
|
+
const table = defineTable(meta.tableName, columns, {}, meta.hooks);
|
|
6024
|
+
meta.table = table;
|
|
6025
|
+
return table;
|
|
6026
|
+
};
|
|
6027
|
+
|
|
6028
|
+
// src/orm/entity-registry.ts
|
|
6029
|
+
var tableToConstructor = /* @__PURE__ */ new Map();
|
|
6030
|
+
var rebuildRegistry = () => {
|
|
6031
|
+
tableToConstructor.clear();
|
|
6032
|
+
for (const meta of getAllEntityMetadata()) {
|
|
6033
|
+
if (meta.table) {
|
|
6034
|
+
tableToConstructor.set(meta.table, meta.target);
|
|
6035
|
+
}
|
|
6036
|
+
}
|
|
6037
|
+
};
|
|
6038
|
+
|
|
6039
|
+
// src/orm/entity-materializer.ts
|
|
6040
|
+
var PrototypeMaterializationStrategy = class {
|
|
6041
|
+
materialize(ctor, data) {
|
|
6042
|
+
const instance = Object.create(ctor.prototype);
|
|
6043
|
+
Object.assign(instance, data);
|
|
6044
|
+
return instance;
|
|
6045
|
+
}
|
|
6046
|
+
};
|
|
6047
|
+
var ConstructorMaterializationStrategy = class {
|
|
6048
|
+
materialize(ctor, data) {
|
|
6049
|
+
const instance = Reflect.construct(ctor, []);
|
|
6050
|
+
Object.assign(instance, data);
|
|
6051
|
+
return instance;
|
|
6052
|
+
}
|
|
6053
|
+
};
|
|
6054
|
+
var DefaultEntityMaterializer = class {
|
|
6055
|
+
constructor(strategy = new ConstructorMaterializationStrategy()) {
|
|
6056
|
+
this.strategy = strategy;
|
|
6057
|
+
}
|
|
6058
|
+
materialize(ctor, row) {
|
|
6059
|
+
const instance = this.strategy.materialize(ctor, row);
|
|
6060
|
+
this.materializeRelations(instance, ctor);
|
|
6061
|
+
return instance;
|
|
6062
|
+
}
|
|
6063
|
+
materializeMany(ctor, rows) {
|
|
6064
|
+
return rows.map((row) => this.materialize(ctor, row));
|
|
6065
|
+
}
|
|
6066
|
+
/**
|
|
6067
|
+
* Recursively materializes nested relation data.
|
|
6068
|
+
*/
|
|
6069
|
+
materializeRelations(instance, _ctor) {
|
|
6070
|
+
rebuildRegistry();
|
|
6071
|
+
for (const [key, value] of Object.entries(instance)) {
|
|
6072
|
+
if (value === null || value === void 0) continue;
|
|
6073
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
6074
|
+
const nested = value;
|
|
6075
|
+
if (this.isEntityLike(nested)) {
|
|
6076
|
+
}
|
|
6077
|
+
}
|
|
6078
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
6079
|
+
const first = value[0];
|
|
6080
|
+
if (typeof first === "object" && first !== null && this.isEntityLike(first)) {
|
|
6081
|
+
}
|
|
6082
|
+
}
|
|
6083
|
+
}
|
|
6084
|
+
}
|
|
6085
|
+
/**
|
|
6086
|
+
* Simple heuristic to check if an object looks like an entity.
|
|
6087
|
+
*/
|
|
6088
|
+
isEntityLike(obj) {
|
|
6089
|
+
return "id" in obj || Object.keys(obj).some(
|
|
6090
|
+
(k) => k.endsWith("Id") || k === "createdAt" || k === "updatedAt"
|
|
6091
|
+
);
|
|
6092
|
+
}
|
|
6093
|
+
};
|
|
6094
|
+
var materializeAs = (ctor, results) => {
|
|
6095
|
+
const materializer = new DefaultEntityMaterializer();
|
|
6096
|
+
return materializer.materializeMany(ctor, results);
|
|
6097
|
+
};
|
|
6098
|
+
|
|
5973
6099
|
// src/query-builder/query-resolution.ts
|
|
5974
6100
|
function resolveSelectQuery(query) {
|
|
5975
6101
|
const candidate = query;
|
|
@@ -6380,6 +6506,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6380
6506
|
relationFacet;
|
|
6381
6507
|
lazyRelations;
|
|
6382
6508
|
lazyRelationOptions;
|
|
6509
|
+
entityConstructor;
|
|
6383
6510
|
/**
|
|
6384
6511
|
* Creates a new SelectQueryBuilder instance
|
|
6385
6512
|
* @param table - Table definition to query
|
|
@@ -6387,7 +6514,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6387
6514
|
* @param hydration - Optional hydration manager
|
|
6388
6515
|
* @param dependencies - Optional query builder dependencies
|
|
6389
6516
|
*/
|
|
6390
|
-
constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions) {
|
|
6517
|
+
constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions, entityConstructor) {
|
|
6391
6518
|
const deps = resolveSelectQueryBuilderDependencies(dependencies);
|
|
6392
6519
|
this.env = { table, deps };
|
|
6393
6520
|
const createAstService = (nextState) => deps.createQueryAstService(table, nextState);
|
|
@@ -6399,6 +6526,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6399
6526
|
};
|
|
6400
6527
|
this.lazyRelations = new Set(lazyRelations ?? []);
|
|
6401
6528
|
this.lazyRelationOptions = new Map(lazyRelationOptions ?? []);
|
|
6529
|
+
this.entityConstructor = entityConstructor;
|
|
6402
6530
|
this.columnSelector = deps.createColumnSelector(this.env);
|
|
6403
6531
|
const relationManager = deps.createRelationManager(this.env);
|
|
6404
6532
|
this.fromFacet = new SelectFromFacet(this.env, createAstService);
|
|
@@ -6422,7 +6550,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6422
6550
|
context.hydration,
|
|
6423
6551
|
this.env.deps,
|
|
6424
6552
|
lazyRelations,
|
|
6425
|
-
lazyRelationOptions
|
|
6553
|
+
lazyRelationOptions,
|
|
6554
|
+
this.entityConstructor
|
|
6426
6555
|
);
|
|
6427
6556
|
}
|
|
6428
6557
|
/**
|
|
@@ -6813,16 +6942,67 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6813
6942
|
return this.env.table;
|
|
6814
6943
|
}
|
|
6815
6944
|
/**
|
|
6816
|
-
*
|
|
6945
|
+
* Ensures that if no columns are selected, all columns from the table are selected by default.
|
|
6946
|
+
*/
|
|
6947
|
+
ensureDefaultSelection() {
|
|
6948
|
+
const columns = this.context.state.ast.columns;
|
|
6949
|
+
if (!columns || columns.length === 0) {
|
|
6950
|
+
return this.select(...Object.keys(this.env.table.columns));
|
|
6951
|
+
}
|
|
6952
|
+
return this;
|
|
6953
|
+
}
|
|
6954
|
+
/**
|
|
6955
|
+
* Executes the query and returns hydrated results.
|
|
6956
|
+
* If the builder was created with an entity constructor (e.g. via selectFromEntity),
|
|
6957
|
+
* this will automatically return fully materialized entity instances.
|
|
6958
|
+
*
|
|
6817
6959
|
* @param ctx - ORM session context
|
|
6818
|
-
* @returns Promise of entity instances
|
|
6960
|
+
* @returns Promise of entity instances (or objects if generic T is not an entity)
|
|
6819
6961
|
* @example
|
|
6820
|
-
* const users = await
|
|
6821
|
-
*
|
|
6822
|
-
*
|
|
6962
|
+
* const users = await selectFromEntity(User).execute(session);
|
|
6963
|
+
* // users is User[]
|
|
6964
|
+
* users[0] instanceof User; // true
|
|
6823
6965
|
*/
|
|
6824
6966
|
async execute(ctx) {
|
|
6825
|
-
|
|
6967
|
+
if (this.entityConstructor) {
|
|
6968
|
+
return this.executeAs(this.entityConstructor, ctx);
|
|
6969
|
+
}
|
|
6970
|
+
const builder = this.ensureDefaultSelection();
|
|
6971
|
+
return executeHydrated(ctx, builder);
|
|
6972
|
+
}
|
|
6973
|
+
/**
|
|
6974
|
+
* Executes the query and returns plain row objects (POJOs), ignoring any entity materialization.
|
|
6975
|
+
* Use this if you want raw data even when using selectFromEntity.
|
|
6976
|
+
*
|
|
6977
|
+
* @param ctx - ORM session context
|
|
6978
|
+
* @returns Promise of plain entity instances
|
|
6979
|
+
* @example
|
|
6980
|
+
* const rows = await selectFromEntity(User).executePlain(session);
|
|
6981
|
+
* // rows is EntityInstance<UserTable>[] (plain objects)
|
|
6982
|
+
* rows[0] instanceof User; // false
|
|
6983
|
+
*/
|
|
6984
|
+
async executePlain(ctx) {
|
|
6985
|
+
const builder = this.ensureDefaultSelection();
|
|
6986
|
+
return executeHydrated(ctx, builder);
|
|
6987
|
+
}
|
|
6988
|
+
/**
|
|
6989
|
+
* Executes the query and returns results as real class instances.
|
|
6990
|
+
* Unlike execute(), this returns actual instances of the decorated entity class
|
|
6991
|
+
* with working methods and proper instanceof checks.
|
|
6992
|
+
* @param entityClass - The entity class constructor
|
|
6993
|
+
* @param ctx - ORM session context
|
|
6994
|
+
* @returns Promise of entity class instances
|
|
6995
|
+
* @example
|
|
6996
|
+
* const users = await selectFromEntity(User)
|
|
6997
|
+
* .include('posts')
|
|
6998
|
+
* .executeAs(User, session);
|
|
6999
|
+
* users[0] instanceof User; // true!
|
|
7000
|
+
* users[0].getFullName(); // works!
|
|
7001
|
+
*/
|
|
7002
|
+
async executeAs(entityClass, ctx) {
|
|
7003
|
+
const builder = this.ensureDefaultSelection();
|
|
7004
|
+
const results = await executeHydrated(ctx, builder);
|
|
7005
|
+
return materializeAs(entityClass, results);
|
|
6826
7006
|
}
|
|
6827
7007
|
/**
|
|
6828
7008
|
* Executes a count query for the current builder without LIMIT/OFFSET clauses.
|
|
@@ -6840,7 +7020,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6840
7020
|
* const { items, totalItems } = await qb.executePaged(session, { page: 1, pageSize: 20 });
|
|
6841
7021
|
*/
|
|
6842
7022
|
async executePaged(session, options) {
|
|
6843
|
-
|
|
7023
|
+
const builder = this.ensureDefaultSelection();
|
|
7024
|
+
return executePagedQuery(builder, session, options, (sess) => this.count(sess));
|
|
6844
7025
|
}
|
|
6845
7026
|
/**
|
|
6846
7027
|
* Executes the query with provided execution and hydration contexts
|
|
@@ -6853,7 +7034,12 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6853
7034
|
* const users = await qb.executeWithContexts(execCtx, hydCtx);
|
|
6854
7035
|
*/
|
|
6855
7036
|
async executeWithContexts(execCtx, hydCtx) {
|
|
6856
|
-
|
|
7037
|
+
const builder = this.ensureDefaultSelection();
|
|
7038
|
+
const results = await executeHydratedWithContexts(execCtx, hydCtx, builder);
|
|
7039
|
+
if (this.entityConstructor) {
|
|
7040
|
+
return materializeAs(this.entityConstructor, results);
|
|
7041
|
+
}
|
|
7042
|
+
return results;
|
|
6857
7043
|
}
|
|
6858
7044
|
/**
|
|
6859
7045
|
* Adds a WHERE condition to the query
|
|
@@ -7146,61 +7332,6 @@ var isTableDef = (value) => {
|
|
|
7146
7332
|
return true;
|
|
7147
7333
|
};
|
|
7148
7334
|
|
|
7149
|
-
// src/orm/entity-metadata.ts
|
|
7150
|
-
var metadataMap = /* @__PURE__ */ new Map();
|
|
7151
|
-
var ensureEntityMetadata = (target) => {
|
|
7152
|
-
let meta = metadataMap.get(target);
|
|
7153
|
-
if (!meta) {
|
|
7154
|
-
meta = {
|
|
7155
|
-
target,
|
|
7156
|
-
tableName: target.name || "unknown",
|
|
7157
|
-
columns: {},
|
|
7158
|
-
relations: {}
|
|
7159
|
-
};
|
|
7160
|
-
metadataMap.set(target, meta);
|
|
7161
|
-
}
|
|
7162
|
-
return meta;
|
|
7163
|
-
};
|
|
7164
|
-
var getEntityMetadata = (target) => {
|
|
7165
|
-
return metadataMap.get(target);
|
|
7166
|
-
};
|
|
7167
|
-
var getAllEntityMetadata = () => {
|
|
7168
|
-
return Array.from(metadataMap.values());
|
|
7169
|
-
};
|
|
7170
|
-
var addColumnMetadata = (target, propertyKey, column) => {
|
|
7171
|
-
const meta = ensureEntityMetadata(target);
|
|
7172
|
-
meta.columns[propertyKey] = { ...column };
|
|
7173
|
-
};
|
|
7174
|
-
var addRelationMetadata = (target, propertyKey, relation) => {
|
|
7175
|
-
const meta = ensureEntityMetadata(target);
|
|
7176
|
-
meta.relations[propertyKey] = relation;
|
|
7177
|
-
};
|
|
7178
|
-
var setEntityTableName = (target, tableName, hooks) => {
|
|
7179
|
-
const meta = ensureEntityMetadata(target);
|
|
7180
|
-
if (tableName && tableName.length > 0) {
|
|
7181
|
-
meta.tableName = tableName;
|
|
7182
|
-
}
|
|
7183
|
-
if (hooks) {
|
|
7184
|
-
meta.hooks = hooks;
|
|
7185
|
-
}
|
|
7186
|
-
};
|
|
7187
|
-
var buildTableDef = (meta) => {
|
|
7188
|
-
if (meta.table) {
|
|
7189
|
-
return meta.table;
|
|
7190
|
-
}
|
|
7191
|
-
const columns = {};
|
|
7192
|
-
for (const [key, def] of Object.entries(meta.columns)) {
|
|
7193
|
-
columns[key] = {
|
|
7194
|
-
...def,
|
|
7195
|
-
name: key,
|
|
7196
|
-
table: meta.tableName
|
|
7197
|
-
};
|
|
7198
|
-
}
|
|
7199
|
-
const table = defineTable(meta.tableName, columns, {}, meta.hooks);
|
|
7200
|
-
meta.table = table;
|
|
7201
|
-
return table;
|
|
7202
|
-
};
|
|
7203
|
-
|
|
7204
7335
|
// src/decorators/bootstrap.ts
|
|
7205
7336
|
var unwrapTarget = (target) => {
|
|
7206
7337
|
if (typeof target === "function" && target.prototype === void 0) {
|
|
@@ -7320,7 +7451,15 @@ var selectFromEntity = (ctor) => {
|
|
|
7320
7451
|
if (!table) {
|
|
7321
7452
|
throw new Error(`Entity '${ctor.name}' is not registered with decorators or has not been bootstrapped`);
|
|
7322
7453
|
}
|
|
7323
|
-
return new SelectQueryBuilder(
|
|
7454
|
+
return new SelectQueryBuilder(
|
|
7455
|
+
table,
|
|
7456
|
+
void 0,
|
|
7457
|
+
void 0,
|
|
7458
|
+
void 0,
|
|
7459
|
+
void 0,
|
|
7460
|
+
void 0,
|
|
7461
|
+
ctor
|
|
7462
|
+
);
|
|
7324
7463
|
};
|
|
7325
7464
|
var entityRef = (ctor) => {
|
|
7326
7465
|
const table = getTableDefFromEntity(ctor);
|
|
@@ -12565,7 +12704,9 @@ export {
|
|
|
12565
12704
|
BelongsTo,
|
|
12566
12705
|
BelongsToMany,
|
|
12567
12706
|
Column,
|
|
12707
|
+
ConstructorMaterializationStrategy,
|
|
12568
12708
|
DefaultBelongsToReference,
|
|
12709
|
+
DefaultEntityMaterializer,
|
|
12569
12710
|
DefaultHasManyCollection,
|
|
12570
12711
|
DefaultManyToManyCollection,
|
|
12571
12712
|
DeleteQueryBuilder,
|
|
@@ -12581,6 +12722,7 @@ export {
|
|
|
12581
12722
|
Pool,
|
|
12582
12723
|
PostgresDialect,
|
|
12583
12724
|
PrimaryKey,
|
|
12725
|
+
PrototypeMaterializationStrategy,
|
|
12584
12726
|
RelationKinds,
|
|
12585
12727
|
STANDARD_COLUMN_TYPES,
|
|
12586
12728
|
SelectQueryBuilder,
|
|
@@ -12732,6 +12874,7 @@ export {
|
|
|
12732
12874
|
lt,
|
|
12733
12875
|
lte,
|
|
12734
12876
|
ltrim,
|
|
12877
|
+
materializeAs,
|
|
12735
12878
|
max,
|
|
12736
12879
|
md5,
|
|
12737
12880
|
min,
|