bigal 15.4.0 → 15.5.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ # [15.5.0](https://github.com/bigalorm/bigal/compare/v15.4.0...v15.5.0) (2026-01-13)
2
+
3
+ ### Features
4
+
5
+ - Add toJSON() method for returning plain objects instead of class instances ([#271](https://github.com/bigalorm/bigal/issues/271)) ([3e2ee5d](https://github.com/bigalorm/bigal/commit/3e2ee5d803d705bc54bc8b08044f5848088563bb))
6
+
1
7
  # [15.4.0](https://github.com/bigalorm/bigal/compare/v15.3.0...v15.4.0) (2026-01-08)
2
8
 
3
9
  ### Features
package/README.md CHANGED
@@ -183,8 +183,8 @@ export function startup({
183
183
  });
184
184
 
185
185
  let categoryRepository: Repository<Category>;
186
- let productRepository: Repository<Category>;
187
- let storeRepository: Repository<Category>;
186
+ let productRepository: Repository<Product>;
187
+ let storeRepository: Repository<Store>;
188
188
  for (const [modelName, repository] = Object.entries(repositoriesByName)) {
189
189
  switch (modelName) {
190
190
  case 'Category':
package/dist/index.cjs CHANGED
@@ -1961,6 +1961,7 @@ class ReadonlyRepository {
1961
1961
  const manuallySetFields = [];
1962
1962
  const sorts = sort ? this._convertSortsToOrderBy(sort) : [];
1963
1963
  const joins = [];
1964
+ let returnAsPlainObjects = false;
1964
1965
  const modelInstance = this;
1965
1966
  return {
1966
1967
  /**
@@ -2050,6 +2051,10 @@ class ReadonlyRepository {
2050
2051
  });
2051
2052
  return this;
2052
2053
  },
2054
+ toJSON() {
2055
+ returnAsPlainObjects = true;
2056
+ return this;
2057
+ },
2053
2058
  async then(resolve, reject) {
2054
2059
  try {
2055
2060
  if (typeof where === "string") {
@@ -2069,9 +2074,10 @@ class ReadonlyRepository {
2069
2074
  const results = await pool.query(query, params);
2070
2075
  const firstResult = results.rows[0];
2071
2076
  if (firstResult) {
2072
- const result = modelInstance._buildInstance(firstResult);
2077
+ const result = returnAsPlainObjects ? modelInstance._buildPlainObject(firstResult) : modelInstance._buildInstance(firstResult);
2073
2078
  if (populates.length) {
2074
- await modelInstance.populateFields([result], populates);
2079
+ const populatesWithFlag = populates.map((pop) => ({ ...pop, asPlainObjects: returnAsPlainObjects }));
2080
+ await modelInstance.populateFields([result], populatesWithFlag);
2075
2081
  }
2076
2082
  for (const manuallySetField of manuallySetFields) {
2077
2083
  result[manuallySetField.propertyName] = manuallySetField.value;
@@ -2150,6 +2156,7 @@ ${stack ?? ""}`;
2150
2156
  const sorts = sort ? this._convertSortsToOrderBy(sort) : [];
2151
2157
  const joins = [];
2152
2158
  let includeCount = false;
2159
+ let returnAsPlainObjects = false;
2153
2160
  const modelInstance = this;
2154
2161
  return {
2155
2162
  /**
@@ -2264,6 +2271,10 @@ ${stack ?? ""}`;
2264
2271
  includeCount = true;
2265
2272
  return this;
2266
2273
  },
2274
+ toJSON() {
2275
+ returnAsPlainObjects = true;
2276
+ return this;
2277
+ },
2267
2278
  async then(resolve, reject) {
2268
2279
  try {
2269
2280
  if (typeof where === "string") {
@@ -2290,9 +2301,10 @@ ${stack ?? ""}`;
2290
2301
  const { __total_count__, ...rest } = row;
2291
2302
  return rest;
2292
2303
  }) : results.rows;
2293
- const entities = modelInstance._buildInstances(rows);
2304
+ const entities = returnAsPlainObjects ? modelInstance._buildPlainObjects(rows) : modelInstance._buildInstances(rows);
2294
2305
  if (populates.length) {
2295
- await modelInstance.populateFields(entities, populates);
2306
+ const populatesWithFlag = populates.map((pop) => ({ ...pop, asPlainObjects: returnAsPlainObjects }));
2307
+ await modelInstance.populateFields(entities, populatesWithFlag);
2296
2308
  }
2297
2309
  if (includeCount) {
2298
2310
  return await resolve({
@@ -2414,6 +2426,40 @@ ${stack ?? ""}`;
2414
2426
  _buildInstances(rows) {
2415
2427
  return rows.map((row) => this._buildInstance(row));
2416
2428
  }
2429
+ _buildPlainObject(row) {
2430
+ const plainObject = { ...row };
2431
+ for (const name of this._floatProperties) {
2432
+ const originalValue = plainObject[name];
2433
+ if (originalValue != null && typeof originalValue === "string") {
2434
+ try {
2435
+ const value = Number(originalValue);
2436
+ if (Number.isFinite(value) && value.toString() === originalValue) {
2437
+ plainObject[name] = value;
2438
+ }
2439
+ } catch {
2440
+ }
2441
+ }
2442
+ }
2443
+ for (const name of this._intProperties) {
2444
+ const originalValue = plainObject[name];
2445
+ if (originalValue != null && typeof originalValue === "string") {
2446
+ try {
2447
+ const value = Number(originalValue);
2448
+ if (Number.isFinite(value) && value.toString() === originalValue) {
2449
+ const valueAsInt = Math.trunc(value);
2450
+ if (Number.isSafeInteger(valueAsInt)) {
2451
+ plainObject[name] = valueAsInt;
2452
+ }
2453
+ }
2454
+ } catch {
2455
+ }
2456
+ }
2457
+ }
2458
+ return plainObject;
2459
+ }
2460
+ _buildPlainObjects(rows) {
2461
+ return rows.map((row) => this._buildPlainObject(row));
2462
+ }
2417
2463
  _convertSortsToOrderBy(sorts) {
2418
2464
  const result = [];
2419
2465
  if (sorts) {
@@ -2530,12 +2576,13 @@ ${stack ?? ""}`;
2530
2576
  [populateRepository.model.primaryKeyColumn.propertyName]: Array.from(populateIds),
2531
2577
  ...populate.where
2532
2578
  };
2533
- const populateResults = await populateRepository.find({
2579
+ const findQuery = populateRepository.find({
2534
2580
  select: populate.select,
2535
2581
  where: populateWhere,
2536
2582
  sort: populate.sort,
2537
2583
  pool: populate.pool
2538
2584
  });
2585
+ const populateResults = populate.asPlainObjects ? await findQuery.toJSON() : await findQuery;
2539
2586
  const populateResultsById = keyBy(populateResults, populateRepository.model.primaryKeyColumn.propertyName);
2540
2587
  for (const entity of entities) {
2541
2588
  entity[propertyName] = populateResultsById[entity[propertyName]];
@@ -2549,7 +2596,7 @@ ${stack ?? ""}`;
2549
2596
  [column.via]: entityIds,
2550
2597
  ...populate.where
2551
2598
  };
2552
- const populateResults = await populateRepository.find({
2599
+ const findQuery = populateRepository.find({
2553
2600
  select: populate.select,
2554
2601
  where: populateWhere,
2555
2602
  sort: populate.sort,
@@ -2557,6 +2604,7 @@ ${stack ?? ""}`;
2557
2604
  limit: populate.limit,
2558
2605
  pool: populate.pool
2559
2606
  });
2607
+ const populateResults = populate.asPlainObjects ? await findQuery.toJSON() : await findQuery;
2560
2608
  if (entities.length === 1) {
2561
2609
  for (const entity of entities) {
2562
2610
  entity[populate.propertyName] = populateResults;
@@ -2609,7 +2657,7 @@ ${stack ?? ""}`;
2609
2657
  [populateModelPrimaryKeyPropertyName]: Array.from(populateIds),
2610
2658
  ...populate.where
2611
2659
  };
2612
- const populateResults = await populateRepository.find({
2660
+ const findQuery = populateRepository.find({
2613
2661
  select: populate.select,
2614
2662
  where: populateWhere,
2615
2663
  sort: populate.sort,
@@ -2617,6 +2665,7 @@ ${stack ?? ""}`;
2617
2665
  limit: populate.limit,
2618
2666
  pool: populate.pool
2619
2667
  });
2668
+ const populateResults = populate.asPlainObjects ? await findQuery.toJSON() : await findQuery;
2620
2669
  const populateResultsById = keyBy(populateResults, populateModelPrimaryKeyPropertyName);
2621
2670
  for (const entity of entities) {
2622
2671
  const populatedItems = [];
package/dist/index.d.cts CHANGED
@@ -85,6 +85,15 @@ type PickFunctions<T> = {
85
85
  [K in keyof T as IncludeFunctions<T[K], K>]: T[K];
86
86
  };
87
87
 
88
+ /**
89
+ * Recursively converts entity types to plain object types.
90
+ * This is primarily for documentation/intent - at runtime, this strips the prototype chain.
91
+ * Preserves Date objects as-is since they're serializable by most frameworks.
92
+ */
93
+ type PlainObject<T> = T extends Date ? Date : T extends (infer U)[] ? PlainObject<U>[] : T extends object ? {
94
+ [K in keyof T]: PlainObject<T[K]>;
95
+ } : T;
96
+
88
97
  /**
89
98
  * Changes all properties with Entity values to Primitive (string|number). Removes any properties that with values
90
99
  * of Entity arrays
@@ -553,6 +562,16 @@ interface PopulateArgs<T extends Entity, K extends keyof T> {
553
562
  pool?: PoolLike;
554
563
  }
555
564
 
565
+ interface FindOneResultJSON<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<PlainObject<TReturn> | null> {
566
+ select<TKeys extends string & keyof T>(keys: TKeys[]): FindOneResultJSON<T, Pick<T, TKeys>, TJoins>;
567
+ where(args: JoinedWhereQuery<T, TJoins>): FindOneResultJSON<T, TReturn, TJoins>;
568
+ populate<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T, TPopulateType extends GetValueType<T[TProperty], Entity>, TPopulateSelectKeys extends string & keyof TPopulateType>(propertyName: TProperty, options?: PopulateArgs<TPopulateType, TPopulateSelectKeys>): FindOneResultJSON<T, Omit<TReturn, TProperty> & Populated<T, TProperty, TPopulateType, TPopulateSelectKeys>, TJoins>;
569
+ join<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias): FindOneResultJSON<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
570
+ leftJoin<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias, on?: WhereQuery<GetValueType<T[TProperty], Entity>>): FindOneResultJSON<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
571
+ sort(value?: JoinedSort<T, TJoins>): FindOneResultJSON<T, TReturn, TJoins>;
572
+ UNSAFE_withOriginalFieldType<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T>(propertyName: TProperty): FindOneResultJSON<T, Omit<TReturn, TProperty> & Pick<T, TProperty>, TJoins>;
573
+ UNSAFE_withFieldValue<TProperty extends string & keyof T, TValue extends T[TProperty]>(propertyName: TProperty, value: TValue): FindOneResultJSON<T, Omit<TReturn, TProperty> & PickAsType<T, TProperty, TValue>, TJoins>;
574
+ }
556
575
  interface FindOneResult<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<TReturn | null> {
557
576
  select<TKeys extends string & keyof T>(keys: TKeys[]): FindOneResult<T, Pick<T, TKeys>, TJoins>;
558
577
  where(args: JoinedWhereQuery<T, TJoins>): FindOneResult<T, TReturn, TJoins>;
@@ -562,6 +581,11 @@ interface FindOneResult<T extends Entity, TReturn, TJoins extends JoinInfo = nev
562
581
  sort(value?: JoinedSort<T, TJoins>): FindOneResult<T, TReturn, TJoins>;
563
582
  UNSAFE_withOriginalFieldType<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T>(propertyName: TProperty): FindOneResult<T, Omit<TReturn, TProperty> & Pick<T, TProperty>, TJoins>;
564
583
  UNSAFE_withFieldValue<TProperty extends string & keyof T, TValue extends T[TProperty]>(propertyName: TProperty, value: TValue): FindOneResult<T, Omit<TReturn, TProperty> & PickAsType<T, TProperty, TValue>, TJoins>;
584
+ /**
585
+ * Returns result as a plain object instead of an entity class instance.
586
+ * Useful for when data must be serializable.
587
+ */
588
+ toJSON(): FindOneResultJSON<T, TReturn, TJoins>;
565
589
  }
566
590
 
567
591
  interface PaginateOptions {
@@ -573,6 +597,17 @@ interface FindWithCountResult<TReturn> {
573
597
  results: TReturn[];
574
598
  totalCount: number;
575
599
  }
600
+ interface FindQueryWithCountJSON<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<FindWithCountResult<PlainObject<TReturn>>> {
601
+ select<TKeys extends string & keyof T>(keys: TKeys[]): FindQueryWithCountJSON<T, Pick<T, TKeys>, TJoins>;
602
+ where(args: JoinedWhereQuery<T, TJoins>): FindQueryWithCountJSON<T, TReturn, TJoins>;
603
+ populate<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T, TPopulateType extends GetValueType<T[TProperty], Entity>, TPopulateSelectKeys extends string & keyof TPopulateType>(propertyName: TProperty, options?: PopulateArgs<TPopulateType, TPopulateSelectKeys>): FindQueryWithCountJSON<T, Omit<TReturn, TProperty> & Populated<T, TProperty, TPopulateType, TPopulateSelectKeys>, TJoins>;
604
+ join<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias): FindQueryWithCountJSON<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
605
+ leftJoin<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias, on?: WhereQuery<GetValueType<T[TProperty], Entity>>): FindQueryWithCountJSON<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
606
+ sort(value?: JoinedSort<T, TJoins>): FindQueryWithCountJSON<T, TReturn, TJoins>;
607
+ limit(value: number): FindQueryWithCountJSON<T, TReturn, TJoins>;
608
+ skip(value: number): FindQueryWithCountJSON<T, TReturn, TJoins>;
609
+ paginate(options: PaginateOptions): FindQueryWithCountJSON<T, TReturn, TJoins>;
610
+ }
576
611
  interface FindQueryWithCount<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<FindWithCountResult<TReturn>> {
577
612
  select<TKeys extends string & keyof T>(keys: TKeys[]): FindQueryWithCount<T, Pick<T, TKeys>, TJoins>;
578
613
  where(args: JoinedWhereQuery<T, TJoins>): FindQueryWithCount<T, TReturn, TJoins>;
@@ -583,8 +618,26 @@ interface FindQueryWithCount<T extends Entity, TReturn, TJoins extends JoinInfo
583
618
  limit(value: number): FindQueryWithCount<T, TReturn, TJoins>;
584
619
  skip(value: number): FindQueryWithCount<T, TReturn, TJoins>;
585
620
  paginate(options: PaginateOptions): FindQueryWithCount<T, TReturn, TJoins>;
621
+ /**
622
+ * Returns results as plain objects instead of entity class instances.
623
+ * Useful for when data must be serializable.
624
+ */
625
+ toJSON(): FindQueryWithCountJSON<T, TReturn, TJoins>;
586
626
  }
587
627
 
628
+ interface FindResultJSON<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<PlainObject<TReturn>[]> {
629
+ select<TKeys extends string & keyof T>(keys: TKeys[]): FindResultJSON<T, Pick<T, TKeys>, TJoins>;
630
+ where(args: JoinedWhereQuery<T, TJoins>): FindResultJSON<T, TReturn, TJoins>;
631
+ populate<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T, TPopulateType extends GetValueType<T[TProperty], Entity>, TPopulateSelectKeys extends string & keyof TPopulateType>(propertyName: TProperty, options?: PopulateArgs<TPopulateType, TPopulateSelectKeys>): FindResultJSON<T, Omit<TReturn, TProperty> & Populated<T, TProperty, TPopulateType, TPopulateSelectKeys>, TJoins>;
632
+ join<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias): FindResultJSON<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
633
+ leftJoin<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias, on?: WhereQuery<GetValueType<T[TProperty], Entity>>): FindResultJSON<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
634
+ sort(value?: JoinedSort<T, TJoins>): FindResultJSON<T, TReturn, TJoins>;
635
+ limit(value: number): FindResultJSON<T, TReturn, TJoins>;
636
+ skip(value: number): FindResultJSON<T, TReturn, TJoins>;
637
+ paginate(options: PaginateOptions): FindResultJSON<T, TReturn, TJoins>;
638
+ withCount(): FindQueryWithCountJSON<T, TReturn, TJoins>;
639
+ UNSAFE_withOriginalFieldType<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T>(propertyName: TProperty): FindResultJSON<T, Omit<TReturn, TProperty> & Pick<T, TProperty>, TJoins>;
640
+ }
588
641
  interface FindResult<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<TReturn[]> {
589
642
  select<TKeys extends string & keyof T>(keys: TKeys[]): FindResult<T, Pick<T, TKeys>, TJoins>;
590
643
  where(args: JoinedWhereQuery<T, TJoins>): FindResult<T, TReturn, TJoins>;
@@ -597,6 +650,11 @@ interface FindResult<T extends Entity, TReturn, TJoins extends JoinInfo = never>
597
650
  paginate(options: PaginateOptions): FindResult<T, TReturn, TJoins>;
598
651
  withCount(): FindQueryWithCount<T, TReturn, TJoins>;
599
652
  UNSAFE_withOriginalFieldType<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T>(propertyName: TProperty): FindResult<T, Omit<TReturn, TProperty> & Pick<T, TProperty>, TJoins>;
653
+ /**
654
+ * Returns results as plain objects instead of entity class instances.
655
+ * Useful for when data must be serializable.
656
+ */
657
+ toJSON(): FindResultJSON<T, TReturn, TJoins>;
600
658
  }
601
659
 
602
660
  interface IRepository<T extends Entity> extends IReadonlyRepository<T> {
@@ -808,6 +866,7 @@ interface Populate {
808
866
  skip?: number;
809
867
  limit?: number;
810
868
  pool?: PoolLike;
869
+ asPlainObjects?: boolean;
811
870
  }
812
871
  declare class ReadonlyRepository<T extends Entity> implements IReadonlyRepository<T> {
813
872
  private readonly _modelMetadata;
@@ -849,6 +908,8 @@ declare class ReadonlyRepository<T extends Entity> implements IReadonlyRepositor
849
908
  count(args?: CountArgs<T> | WhereQuery<T>): CountResult<T>;
850
909
  protected _buildInstance(row: Partial<QueryResult<T>>): QueryResult<T>;
851
910
  protected _buildInstances(rows: Partial<QueryResult<T>>[]): QueryResult<T>[];
911
+ protected _buildPlainObject(row: Partial<QueryResult<T>>): QueryResult<T>;
912
+ protected _buildPlainObjects(rows: Partial<QueryResult<T>>[]): QueryResult<T>[];
852
913
  protected _convertSortsToOrderBy(sorts: SortObject<T> | string): OrderBy<T>[];
853
914
  protected populateFields(entities: QueryResult<T>[], populates: Populate[]): Promise<void>;
854
915
  private populateSingleAssociation;
@@ -1046,4 +1107,4 @@ interface InitializeOptions extends IConnection {
1046
1107
  declare function initialize({ models, pool, readonlyPool, connections, expose }: InitializeOptions): Record<string, IReadonlyRepository<Entity> | IRepository<Entity>>;
1047
1108
 
1048
1109
  export { ColumnBaseMetadata, ColumnCollectionMetadata, ColumnModelMetadata, ColumnTypeMetadata, Entity, ModelMetadata, QueryError, ReadonlyRepository, Repository, ScalarSubquery, SubqueryBuilder, column, createDateColumn, getMetadataStorage, initialize, primaryColumn, subquery, table, updateDateColumn, versionColumn };
1049
- export type { ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindQueryWithCount, FindResult, FindWithCountResult, GetValueType, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, LiteralValues, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, WhereClauseValue, WhereQuery, WhereQueryStatement };
1110
+ export type { ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindOneResultJSON, FindQueryWithCount, FindQueryWithCountJSON, FindResult, FindResultJSON, FindWithCountResult, GetValueType, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, LiteralValues, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PlainObject, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, WhereClauseValue, WhereQuery, WhereQueryStatement };
package/dist/index.d.mts CHANGED
@@ -85,6 +85,15 @@ type PickFunctions<T> = {
85
85
  [K in keyof T as IncludeFunctions<T[K], K>]: T[K];
86
86
  };
87
87
 
88
+ /**
89
+ * Recursively converts entity types to plain object types.
90
+ * This is primarily for documentation/intent - at runtime, this strips the prototype chain.
91
+ * Preserves Date objects as-is since they're serializable by most frameworks.
92
+ */
93
+ type PlainObject<T> = T extends Date ? Date : T extends (infer U)[] ? PlainObject<U>[] : T extends object ? {
94
+ [K in keyof T]: PlainObject<T[K]>;
95
+ } : T;
96
+
88
97
  /**
89
98
  * Changes all properties with Entity values to Primitive (string|number). Removes any properties that with values
90
99
  * of Entity arrays
@@ -553,6 +562,16 @@ interface PopulateArgs<T extends Entity, K extends keyof T> {
553
562
  pool?: PoolLike;
554
563
  }
555
564
 
565
+ interface FindOneResultJSON<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<PlainObject<TReturn> | null> {
566
+ select<TKeys extends string & keyof T>(keys: TKeys[]): FindOneResultJSON<T, Pick<T, TKeys>, TJoins>;
567
+ where(args: JoinedWhereQuery<T, TJoins>): FindOneResultJSON<T, TReturn, TJoins>;
568
+ populate<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T, TPopulateType extends GetValueType<T[TProperty], Entity>, TPopulateSelectKeys extends string & keyof TPopulateType>(propertyName: TProperty, options?: PopulateArgs<TPopulateType, TPopulateSelectKeys>): FindOneResultJSON<T, Omit<TReturn, TProperty> & Populated<T, TProperty, TPopulateType, TPopulateSelectKeys>, TJoins>;
569
+ join<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias): FindOneResultJSON<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
570
+ leftJoin<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias, on?: WhereQuery<GetValueType<T[TProperty], Entity>>): FindOneResultJSON<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
571
+ sort(value?: JoinedSort<T, TJoins>): FindOneResultJSON<T, TReturn, TJoins>;
572
+ UNSAFE_withOriginalFieldType<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T>(propertyName: TProperty): FindOneResultJSON<T, Omit<TReturn, TProperty> & Pick<T, TProperty>, TJoins>;
573
+ UNSAFE_withFieldValue<TProperty extends string & keyof T, TValue extends T[TProperty]>(propertyName: TProperty, value: TValue): FindOneResultJSON<T, Omit<TReturn, TProperty> & PickAsType<T, TProperty, TValue>, TJoins>;
574
+ }
556
575
  interface FindOneResult<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<TReturn | null> {
557
576
  select<TKeys extends string & keyof T>(keys: TKeys[]): FindOneResult<T, Pick<T, TKeys>, TJoins>;
558
577
  where(args: JoinedWhereQuery<T, TJoins>): FindOneResult<T, TReturn, TJoins>;
@@ -562,6 +581,11 @@ interface FindOneResult<T extends Entity, TReturn, TJoins extends JoinInfo = nev
562
581
  sort(value?: JoinedSort<T, TJoins>): FindOneResult<T, TReturn, TJoins>;
563
582
  UNSAFE_withOriginalFieldType<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T>(propertyName: TProperty): FindOneResult<T, Omit<TReturn, TProperty> & Pick<T, TProperty>, TJoins>;
564
583
  UNSAFE_withFieldValue<TProperty extends string & keyof T, TValue extends T[TProperty]>(propertyName: TProperty, value: TValue): FindOneResult<T, Omit<TReturn, TProperty> & PickAsType<T, TProperty, TValue>, TJoins>;
584
+ /**
585
+ * Returns result as a plain object instead of an entity class instance.
586
+ * Useful for when data must be serializable.
587
+ */
588
+ toJSON(): FindOneResultJSON<T, TReturn, TJoins>;
565
589
  }
566
590
 
567
591
  interface PaginateOptions {
@@ -573,6 +597,17 @@ interface FindWithCountResult<TReturn> {
573
597
  results: TReturn[];
574
598
  totalCount: number;
575
599
  }
600
+ interface FindQueryWithCountJSON<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<FindWithCountResult<PlainObject<TReturn>>> {
601
+ select<TKeys extends string & keyof T>(keys: TKeys[]): FindQueryWithCountJSON<T, Pick<T, TKeys>, TJoins>;
602
+ where(args: JoinedWhereQuery<T, TJoins>): FindQueryWithCountJSON<T, TReturn, TJoins>;
603
+ populate<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T, TPopulateType extends GetValueType<T[TProperty], Entity>, TPopulateSelectKeys extends string & keyof TPopulateType>(propertyName: TProperty, options?: PopulateArgs<TPopulateType, TPopulateSelectKeys>): FindQueryWithCountJSON<T, Omit<TReturn, TProperty> & Populated<T, TProperty, TPopulateType, TPopulateSelectKeys>, TJoins>;
604
+ join<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias): FindQueryWithCountJSON<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
605
+ leftJoin<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias, on?: WhereQuery<GetValueType<T[TProperty], Entity>>): FindQueryWithCountJSON<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
606
+ sort(value?: JoinedSort<T, TJoins>): FindQueryWithCountJSON<T, TReturn, TJoins>;
607
+ limit(value: number): FindQueryWithCountJSON<T, TReturn, TJoins>;
608
+ skip(value: number): FindQueryWithCountJSON<T, TReturn, TJoins>;
609
+ paginate(options: PaginateOptions): FindQueryWithCountJSON<T, TReturn, TJoins>;
610
+ }
576
611
  interface FindQueryWithCount<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<FindWithCountResult<TReturn>> {
577
612
  select<TKeys extends string & keyof T>(keys: TKeys[]): FindQueryWithCount<T, Pick<T, TKeys>, TJoins>;
578
613
  where(args: JoinedWhereQuery<T, TJoins>): FindQueryWithCount<T, TReturn, TJoins>;
@@ -583,8 +618,26 @@ interface FindQueryWithCount<T extends Entity, TReturn, TJoins extends JoinInfo
583
618
  limit(value: number): FindQueryWithCount<T, TReturn, TJoins>;
584
619
  skip(value: number): FindQueryWithCount<T, TReturn, TJoins>;
585
620
  paginate(options: PaginateOptions): FindQueryWithCount<T, TReturn, TJoins>;
621
+ /**
622
+ * Returns results as plain objects instead of entity class instances.
623
+ * Useful for when data must be serializable.
624
+ */
625
+ toJSON(): FindQueryWithCountJSON<T, TReturn, TJoins>;
586
626
  }
587
627
 
628
+ interface FindResultJSON<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<PlainObject<TReturn>[]> {
629
+ select<TKeys extends string & keyof T>(keys: TKeys[]): FindResultJSON<T, Pick<T, TKeys>, TJoins>;
630
+ where(args: JoinedWhereQuery<T, TJoins>): FindResultJSON<T, TReturn, TJoins>;
631
+ populate<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T, TPopulateType extends GetValueType<T[TProperty], Entity>, TPopulateSelectKeys extends string & keyof TPopulateType>(propertyName: TProperty, options?: PopulateArgs<TPopulateType, TPopulateSelectKeys>): FindResultJSON<T, Omit<TReturn, TProperty> & Populated<T, TProperty, TPopulateType, TPopulateSelectKeys>, TJoins>;
632
+ join<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias): FindResultJSON<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
633
+ leftJoin<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias, on?: WhereQuery<GetValueType<T[TProperty], Entity>>): FindResultJSON<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
634
+ sort(value?: JoinedSort<T, TJoins>): FindResultJSON<T, TReturn, TJoins>;
635
+ limit(value: number): FindResultJSON<T, TReturn, TJoins>;
636
+ skip(value: number): FindResultJSON<T, TReturn, TJoins>;
637
+ paginate(options: PaginateOptions): FindResultJSON<T, TReturn, TJoins>;
638
+ withCount(): FindQueryWithCountJSON<T, TReturn, TJoins>;
639
+ UNSAFE_withOriginalFieldType<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T>(propertyName: TProperty): FindResultJSON<T, Omit<TReturn, TProperty> & Pick<T, TProperty>, TJoins>;
640
+ }
588
641
  interface FindResult<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<TReturn[]> {
589
642
  select<TKeys extends string & keyof T>(keys: TKeys[]): FindResult<T, Pick<T, TKeys>, TJoins>;
590
643
  where(args: JoinedWhereQuery<T, TJoins>): FindResult<T, TReturn, TJoins>;
@@ -597,6 +650,11 @@ interface FindResult<T extends Entity, TReturn, TJoins extends JoinInfo = never>
597
650
  paginate(options: PaginateOptions): FindResult<T, TReturn, TJoins>;
598
651
  withCount(): FindQueryWithCount<T, TReturn, TJoins>;
599
652
  UNSAFE_withOriginalFieldType<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T>(propertyName: TProperty): FindResult<T, Omit<TReturn, TProperty> & Pick<T, TProperty>, TJoins>;
653
+ /**
654
+ * Returns results as plain objects instead of entity class instances.
655
+ * Useful for when data must be serializable.
656
+ */
657
+ toJSON(): FindResultJSON<T, TReturn, TJoins>;
600
658
  }
601
659
 
602
660
  interface IRepository<T extends Entity> extends IReadonlyRepository<T> {
@@ -808,6 +866,7 @@ interface Populate {
808
866
  skip?: number;
809
867
  limit?: number;
810
868
  pool?: PoolLike;
869
+ asPlainObjects?: boolean;
811
870
  }
812
871
  declare class ReadonlyRepository<T extends Entity> implements IReadonlyRepository<T> {
813
872
  private readonly _modelMetadata;
@@ -849,6 +908,8 @@ declare class ReadonlyRepository<T extends Entity> implements IReadonlyRepositor
849
908
  count(args?: CountArgs<T> | WhereQuery<T>): CountResult<T>;
850
909
  protected _buildInstance(row: Partial<QueryResult<T>>): QueryResult<T>;
851
910
  protected _buildInstances(rows: Partial<QueryResult<T>>[]): QueryResult<T>[];
911
+ protected _buildPlainObject(row: Partial<QueryResult<T>>): QueryResult<T>;
912
+ protected _buildPlainObjects(rows: Partial<QueryResult<T>>[]): QueryResult<T>[];
852
913
  protected _convertSortsToOrderBy(sorts: SortObject<T> | string): OrderBy<T>[];
853
914
  protected populateFields(entities: QueryResult<T>[], populates: Populate[]): Promise<void>;
854
915
  private populateSingleAssociation;
@@ -1046,4 +1107,4 @@ interface InitializeOptions extends IConnection {
1046
1107
  declare function initialize({ models, pool, readonlyPool, connections, expose }: InitializeOptions): Record<string, IReadonlyRepository<Entity> | IRepository<Entity>>;
1047
1108
 
1048
1109
  export { ColumnBaseMetadata, ColumnCollectionMetadata, ColumnModelMetadata, ColumnTypeMetadata, Entity, ModelMetadata, QueryError, ReadonlyRepository, Repository, ScalarSubquery, SubqueryBuilder, column, createDateColumn, getMetadataStorage, initialize, primaryColumn, subquery, table, updateDateColumn, versionColumn };
1049
- export type { ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindQueryWithCount, FindResult, FindWithCountResult, GetValueType, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, LiteralValues, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, WhereClauseValue, WhereQuery, WhereQueryStatement };
1110
+ export type { ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindOneResultJSON, FindQueryWithCount, FindQueryWithCountJSON, FindResult, FindResultJSON, FindWithCountResult, GetValueType, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, LiteralValues, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PlainObject, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, WhereClauseValue, WhereQuery, WhereQueryStatement };
package/dist/index.d.ts CHANGED
@@ -85,6 +85,15 @@ type PickFunctions<T> = {
85
85
  [K in keyof T as IncludeFunctions<T[K], K>]: T[K];
86
86
  };
87
87
 
88
+ /**
89
+ * Recursively converts entity types to plain object types.
90
+ * This is primarily for documentation/intent - at runtime, this strips the prototype chain.
91
+ * Preserves Date objects as-is since they're serializable by most frameworks.
92
+ */
93
+ type PlainObject<T> = T extends Date ? Date : T extends (infer U)[] ? PlainObject<U>[] : T extends object ? {
94
+ [K in keyof T]: PlainObject<T[K]>;
95
+ } : T;
96
+
88
97
  /**
89
98
  * Changes all properties with Entity values to Primitive (string|number). Removes any properties that with values
90
99
  * of Entity arrays
@@ -553,6 +562,16 @@ interface PopulateArgs<T extends Entity, K extends keyof T> {
553
562
  pool?: PoolLike;
554
563
  }
555
564
 
565
+ interface FindOneResultJSON<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<PlainObject<TReturn> | null> {
566
+ select<TKeys extends string & keyof T>(keys: TKeys[]): FindOneResultJSON<T, Pick<T, TKeys>, TJoins>;
567
+ where(args: JoinedWhereQuery<T, TJoins>): FindOneResultJSON<T, TReturn, TJoins>;
568
+ populate<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T, TPopulateType extends GetValueType<T[TProperty], Entity>, TPopulateSelectKeys extends string & keyof TPopulateType>(propertyName: TProperty, options?: PopulateArgs<TPopulateType, TPopulateSelectKeys>): FindOneResultJSON<T, Omit<TReturn, TProperty> & Populated<T, TProperty, TPopulateType, TPopulateSelectKeys>, TJoins>;
569
+ join<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias): FindOneResultJSON<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
570
+ leftJoin<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias, on?: WhereQuery<GetValueType<T[TProperty], Entity>>): FindOneResultJSON<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
571
+ sort(value?: JoinedSort<T, TJoins>): FindOneResultJSON<T, TReturn, TJoins>;
572
+ UNSAFE_withOriginalFieldType<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T>(propertyName: TProperty): FindOneResultJSON<T, Omit<TReturn, TProperty> & Pick<T, TProperty>, TJoins>;
573
+ UNSAFE_withFieldValue<TProperty extends string & keyof T, TValue extends T[TProperty]>(propertyName: TProperty, value: TValue): FindOneResultJSON<T, Omit<TReturn, TProperty> & PickAsType<T, TProperty, TValue>, TJoins>;
574
+ }
556
575
  interface FindOneResult<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<TReturn | null> {
557
576
  select<TKeys extends string & keyof T>(keys: TKeys[]): FindOneResult<T, Pick<T, TKeys>, TJoins>;
558
577
  where(args: JoinedWhereQuery<T, TJoins>): FindOneResult<T, TReturn, TJoins>;
@@ -562,6 +581,11 @@ interface FindOneResult<T extends Entity, TReturn, TJoins extends JoinInfo = nev
562
581
  sort(value?: JoinedSort<T, TJoins>): FindOneResult<T, TReturn, TJoins>;
563
582
  UNSAFE_withOriginalFieldType<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T>(propertyName: TProperty): FindOneResult<T, Omit<TReturn, TProperty> & Pick<T, TProperty>, TJoins>;
564
583
  UNSAFE_withFieldValue<TProperty extends string & keyof T, TValue extends T[TProperty]>(propertyName: TProperty, value: TValue): FindOneResult<T, Omit<TReturn, TProperty> & PickAsType<T, TProperty, TValue>, TJoins>;
584
+ /**
585
+ * Returns result as a plain object instead of an entity class instance.
586
+ * Useful for when data must be serializable.
587
+ */
588
+ toJSON(): FindOneResultJSON<T, TReturn, TJoins>;
565
589
  }
566
590
 
567
591
  interface PaginateOptions {
@@ -573,6 +597,17 @@ interface FindWithCountResult<TReturn> {
573
597
  results: TReturn[];
574
598
  totalCount: number;
575
599
  }
600
+ interface FindQueryWithCountJSON<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<FindWithCountResult<PlainObject<TReturn>>> {
601
+ select<TKeys extends string & keyof T>(keys: TKeys[]): FindQueryWithCountJSON<T, Pick<T, TKeys>, TJoins>;
602
+ where(args: JoinedWhereQuery<T, TJoins>): FindQueryWithCountJSON<T, TReturn, TJoins>;
603
+ populate<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T, TPopulateType extends GetValueType<T[TProperty], Entity>, TPopulateSelectKeys extends string & keyof TPopulateType>(propertyName: TProperty, options?: PopulateArgs<TPopulateType, TPopulateSelectKeys>): FindQueryWithCountJSON<T, Omit<TReturn, TProperty> & Populated<T, TProperty, TPopulateType, TPopulateSelectKeys>, TJoins>;
604
+ join<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias): FindQueryWithCountJSON<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
605
+ leftJoin<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias, on?: WhereQuery<GetValueType<T[TProperty], Entity>>): FindQueryWithCountJSON<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
606
+ sort(value?: JoinedSort<T, TJoins>): FindQueryWithCountJSON<T, TReturn, TJoins>;
607
+ limit(value: number): FindQueryWithCountJSON<T, TReturn, TJoins>;
608
+ skip(value: number): FindQueryWithCountJSON<T, TReturn, TJoins>;
609
+ paginate(options: PaginateOptions): FindQueryWithCountJSON<T, TReturn, TJoins>;
610
+ }
576
611
  interface FindQueryWithCount<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<FindWithCountResult<TReturn>> {
577
612
  select<TKeys extends string & keyof T>(keys: TKeys[]): FindQueryWithCount<T, Pick<T, TKeys>, TJoins>;
578
613
  where(args: JoinedWhereQuery<T, TJoins>): FindQueryWithCount<T, TReturn, TJoins>;
@@ -583,8 +618,26 @@ interface FindQueryWithCount<T extends Entity, TReturn, TJoins extends JoinInfo
583
618
  limit(value: number): FindQueryWithCount<T, TReturn, TJoins>;
584
619
  skip(value: number): FindQueryWithCount<T, TReturn, TJoins>;
585
620
  paginate(options: PaginateOptions): FindQueryWithCount<T, TReturn, TJoins>;
621
+ /**
622
+ * Returns results as plain objects instead of entity class instances.
623
+ * Useful for when data must be serializable.
624
+ */
625
+ toJSON(): FindQueryWithCountJSON<T, TReturn, TJoins>;
586
626
  }
587
627
 
628
+ interface FindResultJSON<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<PlainObject<TReturn>[]> {
629
+ select<TKeys extends string & keyof T>(keys: TKeys[]): FindResultJSON<T, Pick<T, TKeys>, TJoins>;
630
+ where(args: JoinedWhereQuery<T, TJoins>): FindResultJSON<T, TReturn, TJoins>;
631
+ populate<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T, TPopulateType extends GetValueType<T[TProperty], Entity>, TPopulateSelectKeys extends string & keyof TPopulateType>(propertyName: TProperty, options?: PopulateArgs<TPopulateType, TPopulateSelectKeys>): FindResultJSON<T, Omit<TReturn, TProperty> & Populated<T, TProperty, TPopulateType, TPopulateSelectKeys>, TJoins>;
632
+ join<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias): FindResultJSON<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
633
+ leftJoin<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias, on?: WhereQuery<GetValueType<T[TProperty], Entity>>): FindResultJSON<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
634
+ sort(value?: JoinedSort<T, TJoins>): FindResultJSON<T, TReturn, TJoins>;
635
+ limit(value: number): FindResultJSON<T, TReturn, TJoins>;
636
+ skip(value: number): FindResultJSON<T, TReturn, TJoins>;
637
+ paginate(options: PaginateOptions): FindResultJSON<T, TReturn, TJoins>;
638
+ withCount(): FindQueryWithCountJSON<T, TReturn, TJoins>;
639
+ UNSAFE_withOriginalFieldType<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T>(propertyName: TProperty): FindResultJSON<T, Omit<TReturn, TProperty> & Pick<T, TProperty>, TJoins>;
640
+ }
588
641
  interface FindResult<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<TReturn[]> {
589
642
  select<TKeys extends string & keyof T>(keys: TKeys[]): FindResult<T, Pick<T, TKeys>, TJoins>;
590
643
  where(args: JoinedWhereQuery<T, TJoins>): FindResult<T, TReturn, TJoins>;
@@ -597,6 +650,11 @@ interface FindResult<T extends Entity, TReturn, TJoins extends JoinInfo = never>
597
650
  paginate(options: PaginateOptions): FindResult<T, TReturn, TJoins>;
598
651
  withCount(): FindQueryWithCount<T, TReturn, TJoins>;
599
652
  UNSAFE_withOriginalFieldType<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T>(propertyName: TProperty): FindResult<T, Omit<TReturn, TProperty> & Pick<T, TProperty>, TJoins>;
653
+ /**
654
+ * Returns results as plain objects instead of entity class instances.
655
+ * Useful for when data must be serializable.
656
+ */
657
+ toJSON(): FindResultJSON<T, TReturn, TJoins>;
600
658
  }
601
659
 
602
660
  interface IRepository<T extends Entity> extends IReadonlyRepository<T> {
@@ -808,6 +866,7 @@ interface Populate {
808
866
  skip?: number;
809
867
  limit?: number;
810
868
  pool?: PoolLike;
869
+ asPlainObjects?: boolean;
811
870
  }
812
871
  declare class ReadonlyRepository<T extends Entity> implements IReadonlyRepository<T> {
813
872
  private readonly _modelMetadata;
@@ -849,6 +908,8 @@ declare class ReadonlyRepository<T extends Entity> implements IReadonlyRepositor
849
908
  count(args?: CountArgs<T> | WhereQuery<T>): CountResult<T>;
850
909
  protected _buildInstance(row: Partial<QueryResult<T>>): QueryResult<T>;
851
910
  protected _buildInstances(rows: Partial<QueryResult<T>>[]): QueryResult<T>[];
911
+ protected _buildPlainObject(row: Partial<QueryResult<T>>): QueryResult<T>;
912
+ protected _buildPlainObjects(rows: Partial<QueryResult<T>>[]): QueryResult<T>[];
852
913
  protected _convertSortsToOrderBy(sorts: SortObject<T> | string): OrderBy<T>[];
853
914
  protected populateFields(entities: QueryResult<T>[], populates: Populate[]): Promise<void>;
854
915
  private populateSingleAssociation;
@@ -1046,4 +1107,4 @@ interface InitializeOptions extends IConnection {
1046
1107
  declare function initialize({ models, pool, readonlyPool, connections, expose }: InitializeOptions): Record<string, IReadonlyRepository<Entity> | IRepository<Entity>>;
1047
1108
 
1048
1109
  export { ColumnBaseMetadata, ColumnCollectionMetadata, ColumnModelMetadata, ColumnTypeMetadata, Entity, ModelMetadata, QueryError, ReadonlyRepository, Repository, ScalarSubquery, SubqueryBuilder, column, createDateColumn, getMetadataStorage, initialize, primaryColumn, subquery, table, updateDateColumn, versionColumn };
1049
- export type { ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindQueryWithCount, FindResult, FindWithCountResult, GetValueType, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, LiteralValues, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, WhereClauseValue, WhereQuery, WhereQueryStatement };
1110
+ export type { ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindOneResultJSON, FindQueryWithCount, FindQueryWithCountJSON, FindResult, FindResultJSON, FindWithCountResult, GetValueType, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, LiteralValues, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PlainObject, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, WhereClauseValue, WhereQuery, WhereQueryStatement };
package/dist/index.mjs CHANGED
@@ -1959,6 +1959,7 @@ class ReadonlyRepository {
1959
1959
  const manuallySetFields = [];
1960
1960
  const sorts = sort ? this._convertSortsToOrderBy(sort) : [];
1961
1961
  const joins = [];
1962
+ let returnAsPlainObjects = false;
1962
1963
  const modelInstance = this;
1963
1964
  return {
1964
1965
  /**
@@ -2048,6 +2049,10 @@ class ReadonlyRepository {
2048
2049
  });
2049
2050
  return this;
2050
2051
  },
2052
+ toJSON() {
2053
+ returnAsPlainObjects = true;
2054
+ return this;
2055
+ },
2051
2056
  async then(resolve, reject) {
2052
2057
  try {
2053
2058
  if (typeof where === "string") {
@@ -2067,9 +2072,10 @@ class ReadonlyRepository {
2067
2072
  const results = await pool.query(query, params);
2068
2073
  const firstResult = results.rows[0];
2069
2074
  if (firstResult) {
2070
- const result = modelInstance._buildInstance(firstResult);
2075
+ const result = returnAsPlainObjects ? modelInstance._buildPlainObject(firstResult) : modelInstance._buildInstance(firstResult);
2071
2076
  if (populates.length) {
2072
- await modelInstance.populateFields([result], populates);
2077
+ const populatesWithFlag = populates.map((pop) => ({ ...pop, asPlainObjects: returnAsPlainObjects }));
2078
+ await modelInstance.populateFields([result], populatesWithFlag);
2073
2079
  }
2074
2080
  for (const manuallySetField of manuallySetFields) {
2075
2081
  result[manuallySetField.propertyName] = manuallySetField.value;
@@ -2148,6 +2154,7 @@ ${stack ?? ""}`;
2148
2154
  const sorts = sort ? this._convertSortsToOrderBy(sort) : [];
2149
2155
  const joins = [];
2150
2156
  let includeCount = false;
2157
+ let returnAsPlainObjects = false;
2151
2158
  const modelInstance = this;
2152
2159
  return {
2153
2160
  /**
@@ -2262,6 +2269,10 @@ ${stack ?? ""}`;
2262
2269
  includeCount = true;
2263
2270
  return this;
2264
2271
  },
2272
+ toJSON() {
2273
+ returnAsPlainObjects = true;
2274
+ return this;
2275
+ },
2265
2276
  async then(resolve, reject) {
2266
2277
  try {
2267
2278
  if (typeof where === "string") {
@@ -2288,9 +2299,10 @@ ${stack ?? ""}`;
2288
2299
  const { __total_count__, ...rest } = row;
2289
2300
  return rest;
2290
2301
  }) : results.rows;
2291
- const entities = modelInstance._buildInstances(rows);
2302
+ const entities = returnAsPlainObjects ? modelInstance._buildPlainObjects(rows) : modelInstance._buildInstances(rows);
2292
2303
  if (populates.length) {
2293
- await modelInstance.populateFields(entities, populates);
2304
+ const populatesWithFlag = populates.map((pop) => ({ ...pop, asPlainObjects: returnAsPlainObjects }));
2305
+ await modelInstance.populateFields(entities, populatesWithFlag);
2294
2306
  }
2295
2307
  if (includeCount) {
2296
2308
  return await resolve({
@@ -2412,6 +2424,40 @@ ${stack ?? ""}`;
2412
2424
  _buildInstances(rows) {
2413
2425
  return rows.map((row) => this._buildInstance(row));
2414
2426
  }
2427
+ _buildPlainObject(row) {
2428
+ const plainObject = { ...row };
2429
+ for (const name of this._floatProperties) {
2430
+ const originalValue = plainObject[name];
2431
+ if (originalValue != null && typeof originalValue === "string") {
2432
+ try {
2433
+ const value = Number(originalValue);
2434
+ if (Number.isFinite(value) && value.toString() === originalValue) {
2435
+ plainObject[name] = value;
2436
+ }
2437
+ } catch {
2438
+ }
2439
+ }
2440
+ }
2441
+ for (const name of this._intProperties) {
2442
+ const originalValue = plainObject[name];
2443
+ if (originalValue != null && typeof originalValue === "string") {
2444
+ try {
2445
+ const value = Number(originalValue);
2446
+ if (Number.isFinite(value) && value.toString() === originalValue) {
2447
+ const valueAsInt = Math.trunc(value);
2448
+ if (Number.isSafeInteger(valueAsInt)) {
2449
+ plainObject[name] = valueAsInt;
2450
+ }
2451
+ }
2452
+ } catch {
2453
+ }
2454
+ }
2455
+ }
2456
+ return plainObject;
2457
+ }
2458
+ _buildPlainObjects(rows) {
2459
+ return rows.map((row) => this._buildPlainObject(row));
2460
+ }
2415
2461
  _convertSortsToOrderBy(sorts) {
2416
2462
  const result = [];
2417
2463
  if (sorts) {
@@ -2528,12 +2574,13 @@ ${stack ?? ""}`;
2528
2574
  [populateRepository.model.primaryKeyColumn.propertyName]: Array.from(populateIds),
2529
2575
  ...populate.where
2530
2576
  };
2531
- const populateResults = await populateRepository.find({
2577
+ const findQuery = populateRepository.find({
2532
2578
  select: populate.select,
2533
2579
  where: populateWhere,
2534
2580
  sort: populate.sort,
2535
2581
  pool: populate.pool
2536
2582
  });
2583
+ const populateResults = populate.asPlainObjects ? await findQuery.toJSON() : await findQuery;
2537
2584
  const populateResultsById = keyBy(populateResults, populateRepository.model.primaryKeyColumn.propertyName);
2538
2585
  for (const entity of entities) {
2539
2586
  entity[propertyName] = populateResultsById[entity[propertyName]];
@@ -2547,7 +2594,7 @@ ${stack ?? ""}`;
2547
2594
  [column.via]: entityIds,
2548
2595
  ...populate.where
2549
2596
  };
2550
- const populateResults = await populateRepository.find({
2597
+ const findQuery = populateRepository.find({
2551
2598
  select: populate.select,
2552
2599
  where: populateWhere,
2553
2600
  sort: populate.sort,
@@ -2555,6 +2602,7 @@ ${stack ?? ""}`;
2555
2602
  limit: populate.limit,
2556
2603
  pool: populate.pool
2557
2604
  });
2605
+ const populateResults = populate.asPlainObjects ? await findQuery.toJSON() : await findQuery;
2558
2606
  if (entities.length === 1) {
2559
2607
  for (const entity of entities) {
2560
2608
  entity[populate.propertyName] = populateResults;
@@ -2607,7 +2655,7 @@ ${stack ?? ""}`;
2607
2655
  [populateModelPrimaryKeyPropertyName]: Array.from(populateIds),
2608
2656
  ...populate.where
2609
2657
  };
2610
- const populateResults = await populateRepository.find({
2658
+ const findQuery = populateRepository.find({
2611
2659
  select: populate.select,
2612
2660
  where: populateWhere,
2613
2661
  sort: populate.sort,
@@ -2615,6 +2663,7 @@ ${stack ?? ""}`;
2615
2663
  limit: populate.limit,
2616
2664
  pool: populate.pool
2617
2665
  });
2666
+ const populateResults = populate.asPlainObjects ? await findQuery.toJSON() : await findQuery;
2618
2667
  const populateResultsById = keyBy(populateResults, populateModelPrimaryKeyPropertyName);
2619
2668
  for (const entity of entities) {
2620
2669
  const populatedItems = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bigal",
3
- "version": "15.4.0",
3
+ "version": "15.5.0",
4
4
  "description": "A fast and lightweight orm for postgres and node.js, written in typescript.",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -46,7 +46,7 @@
46
46
  "@types/node": ">=20",
47
47
  "chai": "6.2.2",
48
48
  "eslint": "9.39.2",
49
- "eslint-config-decent": "3.1.90",
49
+ "eslint-config-decent": "3.1.93",
50
50
  "husky": "9.1.7",
51
51
  "lint-staged": "16.2.7",
52
52
  "markdownlint-cli": "0.47.0",
@@ -56,7 +56,7 @@
56
56
  "prettier": "3.7.4",
57
57
  "semantic-release": "25.0.2",
58
58
  "strict-event-emitter-types": "2.0.0",
59
- "postgres-pool": "11.0.1",
59
+ "postgres-pool": "11.0.2",
60
60
  "ts-mockito": "2.6.1",
61
61
  "ts-node": "10.9.2",
62
62
  "typescript": "5.9.3",