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/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
- * Executes the query and returns hydrated results
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 qb.select('id', 'name')
6821
- * .where(eq(userTable.columns.active, true))
6822
- * .execute(session);
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
- return executeHydrated(ctx, this);
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
- return executePagedQuery(this, session, options, (sess) => this.count(sess));
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
- return executeHydratedWithContexts(execCtx, hydCtx, this);
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(table);
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,