metal-orm 1.0.16 → 1.0.18

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.
Files changed (64) hide show
  1. package/README.md +37 -40
  2. package/dist/decorators/index.cjs +344 -69
  3. package/dist/decorators/index.cjs.map +1 -1
  4. package/dist/decorators/index.d.cts +1 -1
  5. package/dist/decorators/index.d.ts +1 -1
  6. package/dist/decorators/index.js +344 -69
  7. package/dist/decorators/index.js.map +1 -1
  8. package/dist/index.cjs +567 -181
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +66 -30
  11. package/dist/index.d.ts +66 -30
  12. package/dist/index.js +559 -181
  13. package/dist/index.js.map +1 -1
  14. package/dist/{select-BKZrMRCQ.d.cts → select-BuMpVcVt.d.cts} +265 -74
  15. package/dist/{select-BKZrMRCQ.d.ts → select-BuMpVcVt.d.ts} +265 -74
  16. package/package.json +5 -1
  17. package/src/codegen/naming-strategy.ts +15 -10
  18. package/src/core/ast/aggregate-functions.ts +50 -4
  19. package/src/core/ast/builders.ts +23 -3
  20. package/src/core/ast/expression-builders.ts +36 -16
  21. package/src/core/ast/expression-nodes.ts +17 -9
  22. package/src/core/ast/join-node.ts +5 -3
  23. package/src/core/ast/join.ts +16 -16
  24. package/src/core/ast/query.ts +44 -29
  25. package/src/core/ddl/dialects/mssql-schema-dialect.ts +18 -0
  26. package/src/core/ddl/dialects/mysql-schema-dialect.ts +11 -0
  27. package/src/core/ddl/dialects/postgres-schema-dialect.ts +9 -0
  28. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +9 -0
  29. package/src/core/ddl/introspect/functions/postgres.ts +2 -6
  30. package/src/core/dialect/abstract.ts +12 -8
  31. package/src/core/dialect/base/sql-dialect.ts +58 -46
  32. package/src/core/dialect/mssql/functions.ts +24 -15
  33. package/src/core/dialect/mssql/index.ts +53 -28
  34. package/src/core/dialect/postgres/functions.ts +33 -24
  35. package/src/core/dialect/sqlite/functions.ts +19 -12
  36. package/src/core/dialect/sqlite/index.ts +22 -13
  37. package/src/core/functions/datetime.ts +2 -1
  38. package/src/core/functions/numeric.ts +2 -1
  39. package/src/core/functions/standard-strategy.ts +52 -12
  40. package/src/core/functions/text.ts +2 -1
  41. package/src/core/functions/types.ts +8 -8
  42. package/src/index.ts +5 -4
  43. package/src/orm/domain-event-bus.ts +43 -25
  44. package/src/orm/entity-meta.ts +40 -0
  45. package/src/orm/execution-context.ts +6 -0
  46. package/src/orm/hydration-context.ts +6 -4
  47. package/src/orm/orm-session.ts +35 -24
  48. package/src/orm/orm.ts +10 -10
  49. package/src/orm/query-logger.ts +15 -0
  50. package/src/orm/runtime-types.ts +60 -2
  51. package/src/orm/transaction-runner.ts +7 -0
  52. package/src/orm/unit-of-work.ts +1 -0
  53. package/src/query-builder/column-selector.ts +9 -7
  54. package/src/query-builder/insert-query-state.ts +13 -3
  55. package/src/query-builder/query-ast-service.ts +59 -38
  56. package/src/query-builder/relation-conditions.ts +38 -34
  57. package/src/query-builder/relation-manager.ts +8 -3
  58. package/src/query-builder/relation-service.ts +59 -46
  59. package/src/query-builder/select-helpers.ts +50 -0
  60. package/src/query-builder/select-query-state.ts +19 -7
  61. package/src/query-builder/select.ts +339 -167
  62. package/src/query-builder/update-query-state.ts +31 -9
  63. package/src/schema/column.ts +75 -39
  64. package/src/schema/types.ts +17 -6
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Supported column data types for database schema definitions
3
3
  */
4
- type ColumnType = 'INT' | 'INTEGER' | 'BIGINT' | 'VARCHAR' | 'TEXT' | 'JSON' | 'ENUM' | 'DECIMAL' | 'FLOAT' | 'DOUBLE' | 'UUID' | 'DATE' | 'DATETIME' | 'TIMESTAMP' | 'TIMESTAMPTZ' | 'BOOLEAN' | 'int' | 'integer' | 'bigint' | 'varchar' | 'text' | 'json' | 'enum' | 'decimal' | 'float' | 'double' | 'uuid' | 'date' | 'datetime' | 'timestamp' | 'timestamptz' | 'boolean';
4
+ type ColumnType = 'INT' | 'INTEGER' | 'BIGINT' | 'VARCHAR' | 'TEXT' | 'JSON' | 'ENUM' | 'DECIMAL' | 'FLOAT' | 'DOUBLE' | 'UUID' | 'BINARY' | 'VARBINARY' | 'BLOB' | 'BYTEA' | 'DATE' | 'DATETIME' | 'TIMESTAMP' | 'TIMESTAMPTZ' | 'BOOLEAN' | 'int' | 'integer' | 'bigint' | 'varchar' | 'text' | 'json' | 'enum' | 'decimal' | 'float' | 'double' | 'uuid' | 'binary' | 'varbinary' | 'blob' | 'bytea' | 'date' | 'datetime' | 'timestamp' | 'timestamptz' | 'boolean';
5
5
  type ReferentialAction = 'NO ACTION' | 'RESTRICT' | 'CASCADE' | 'SET NULL' | 'SET DEFAULT';
6
6
  interface RawDefaultValue {
7
7
  raw: string;
@@ -83,6 +83,22 @@ declare const col: {
83
83
  * Creates a UUID column definition
84
84
  */
85
85
  uuid: () => ColumnDef<"UUID">;
86
+ /**
87
+ * Creates a binary large object column definition
88
+ */
89
+ blob: () => ColumnDef<"BLOB">;
90
+ /**
91
+ * Creates a fixed-length binary column definition
92
+ */
93
+ binary: (length?: number) => ColumnDef<"BINARY">;
94
+ /**
95
+ * Creates a variable-length binary column definition
96
+ */
97
+ varbinary: (length?: number) => ColumnDef<"VARBINARY">;
98
+ /**
99
+ * Creates a Postgres bytea column definition
100
+ */
101
+ bytea: () => ColumnDef<"BYTEA">;
86
102
  /**
87
103
  * Creates a timestamp column definition
88
104
  */
@@ -348,10 +364,14 @@ interface TableDef<T extends Record<string, ColumnDef> = Record<string, ColumnDe
348
364
  */
349
365
  declare const defineTable: <T extends Record<string, ColumnDef>>(name: string, columns: T, relations?: Record<string, RelationDef>, hooks?: TableHooks, options?: TableOptions) => TableDef<T>;
350
366
 
367
+ /**
368
+ * Resolves a relation definition to its target table type.
369
+ */
370
+ type RelationTargetTable<TRel extends RelationDef> = TRel extends HasManyRelation<infer TTarget> ? TTarget : TRel extends HasOneRelation<infer TTarget> ? TTarget : TRel extends BelongsToRelation<infer TTarget> ? TTarget : TRel extends BelongsToManyRelation<infer TTarget> ? TTarget : never;
351
371
  /**
352
372
  * Maps a ColumnDef to its TypeScript type representation
353
373
  */
354
- type ColumnToTs<T extends ColumnDef> = T['type'] extends 'INT' | 'INTEGER' | 'int' | 'integer' ? number : T['type'] extends 'BIGINT' | 'bigint' ? number | bigint : T['type'] extends 'DECIMAL' | 'decimal' | 'FLOAT' | 'float' | 'DOUBLE' | 'double' ? number : T['type'] extends 'BOOLEAN' | 'boolean' ? boolean : T['type'] extends 'JSON' | 'json' ? unknown : T['type'] extends 'DATE' | 'date' | 'DATETIME' | 'datetime' | 'TIMESTAMP' | 'timestamp' | 'TIMESTAMPTZ' | 'timestamptz' ? string : string;
374
+ type ColumnToTs<T extends ColumnDef> = T['type'] extends 'INT' | 'INTEGER' | 'int' | 'integer' ? number : T['type'] extends 'BIGINT' | 'bigint' ? number | bigint : T['type'] extends 'DECIMAL' | 'decimal' | 'FLOAT' | 'float' | 'DOUBLE' | 'double' ? number : T['type'] extends 'BOOLEAN' | 'boolean' ? boolean : T['type'] extends 'JSON' | 'json' ? unknown : T['type'] extends 'BLOB' | 'blob' | 'BINARY' | 'binary' | 'VARBINARY' | 'varbinary' | 'BYTEA' | 'bytea' ? Buffer : T['type'] extends 'DATE' | 'date' | 'DATETIME' | 'datetime' | 'TIMESTAMP' | 'timestamp' | 'TIMESTAMPTZ' | 'timestamptz' ? string : string;
355
375
  /**
356
376
  * Infers a row shape from a table definition
357
377
  */
@@ -530,6 +550,8 @@ interface ColumnNode {
530
550
  name: string;
531
551
  /** Optional alias for the column */
532
552
  alias?: string;
553
+ /** Optional scope marker (e.g., 'outer' for correlated references) */
554
+ scope?: 'outer' | 'default';
533
555
  }
534
556
  /**
535
557
  * AST node representing a function call
@@ -544,6 +566,12 @@ interface FunctionNode {
544
566
  args: OperandNode[];
545
567
  /** Optional alias for the function result */
546
568
  alias?: string;
569
+ /** Optional ORDER BY clause used by aggregations like GROUP_CONCAT */
570
+ orderBy?: OrderByNode[];
571
+ /** Optional separator argument used by GROUP_CONCAT-like functions */
572
+ separator?: OperandNode;
573
+ /** Optional DISTINCT modifier */
574
+ distinct?: boolean;
547
575
  }
548
576
  /**
549
577
  * AST node representing a JSON path expression
@@ -690,7 +718,7 @@ interface JoinNode {
690
718
  /** Type of join (INNER, LEFT, RIGHT, etc.) */
691
719
  kind: JoinKind;
692
720
  /** Table to join */
693
- table: TableNode | FunctionTableNode;
721
+ table: TableSourceNode;
694
722
  /** Join condition expression */
695
723
  condition: ExpressionNode;
696
724
  /** Optional metadata for non-SQL concerns (e.g., relation name) */
@@ -729,6 +757,19 @@ interface FunctionTableNode {
729
757
  /** Optional column aliases */
730
758
  columnAliases?: string[];
731
759
  }
760
+ /**
761
+ * AST node representing a derived table (subquery with an alias)
762
+ */
763
+ interface DerivedTableNode {
764
+ type: 'DerivedTable';
765
+ /** Subquery providing the rows */
766
+ query: SelectQueryNode;
767
+ /** Required alias for the derived table */
768
+ alias: string;
769
+ /** Optional column aliases */
770
+ columnAliases?: string[];
771
+ }
772
+ type TableSourceNode = TableNode | FunctionTableNode | DerivedTableNode;
732
773
  /**
733
774
  * AST node representing an ORDER BY clause
734
775
  */
@@ -775,7 +816,7 @@ interface SelectQueryNode {
775
816
  /** Optional CTEs (WITH clauses) */
776
817
  ctes?: CommonTableExpressionNode[];
777
818
  /** FROM clause table (either a Table or a FunctionTable) */
778
- from: TableNode | FunctionTableNode;
819
+ from: TableSourceNode;
779
820
  /** SELECT clause columns */
780
821
  columns: (ColumnNode | FunctionNode | ScalarSubqueryNode | CaseExpressionNode | WindowFunctionNode)[];
781
822
  /** JOIN clauses */
@@ -893,6 +934,8 @@ interface HydrationMetadata {
893
934
  interface FunctionRenderContext {
894
935
  node: FunctionNode;
895
936
  compiledArgs: string[];
937
+ /** Helper to compile additional operands (e.g., separators or ORDER BY columns) */
938
+ compileOperand: (operand: OperandNode) => string;
896
939
  }
897
940
  type FunctionRenderer = (ctx: FunctionRenderContext) => string;
898
941
  interface FunctionStrategy {
@@ -1106,6 +1149,12 @@ declare class SelectQueryState {
1106
1149
  * @returns New SelectQueryState with added join
1107
1150
  */
1108
1151
  withJoin(join: JoinNode): SelectQueryState;
1152
+ /**
1153
+ * Replaces the FROM clause.
1154
+ * @param from - Table source for the FROM clause
1155
+ * @returns New SelectQueryState with updated FROM
1156
+ */
1157
+ withFrom(from: TableSourceNode): SelectQueryState;
1109
1158
  /**
1110
1159
  * Adds a WHERE clause to the query
1111
1160
  * @param predicate - WHERE predicate expression
@@ -1342,6 +1391,12 @@ declare class QueryAstService {
1342
1391
  * @returns Updated query state with set operation
1343
1392
  */
1344
1393
  withSetOperation(operator: SetOperationKind, query: SelectQueryNode): SelectQueryState;
1394
+ /**
1395
+ * Replaces the FROM clause for the current query.
1396
+ * @param from - Table source to use in the FROM clause
1397
+ * @returns Updated query state with new FROM
1398
+ */
1399
+ withFrom(from: TableSourceNode): SelectQueryState;
1345
1400
  /**
1346
1401
  * Selects a subquery as a column
1347
1402
  * @param alias - Alias for the subquery
@@ -1483,7 +1538,7 @@ declare class RelationService {
1483
1538
  * @param ast - Query AST to modify
1484
1539
  * @returns Modified query AST with relation correlation
1485
1540
  */
1486
- applyRelationCorrelation(relationName: string, ast: SelectQueryNode): SelectQueryNode;
1541
+ applyRelationCorrelation(relationName: string, ast: SelectQueryNode, additionalCorrelation?: ExpressionNode): SelectQueryNode;
1487
1542
  /**
1488
1543
  * Creates a join node for a relation
1489
1544
  * @param state - Current query state
@@ -1514,6 +1569,7 @@ declare class RelationService {
1514
1569
  * @returns QueryAstService instance
1515
1570
  */
1516
1571
  private astService;
1572
+ private rootTableName;
1517
1573
  }
1518
1574
 
1519
1575
  /**
@@ -1587,6 +1643,101 @@ type EntityConstructor = new (...args: any[]) => any;
1587
1643
  type EntityOrTableTarget = EntityConstructor | TableDef;
1588
1644
  type EntityOrTableTargetResolver = EntityOrTableTarget | (() => EntityOrTableTarget);
1589
1645
 
1646
+ /**
1647
+ * Entity status enum representing the lifecycle state of an entity
1648
+ */
1649
+ declare enum EntityStatus {
1650
+ /** Entity is newly created and not yet persisted */
1651
+ New = "new",
1652
+ /** Entity is managed by the ORM and synchronized with the database */
1653
+ Managed = "managed",
1654
+ /** Entity has been modified but not yet persisted */
1655
+ Dirty = "dirty",
1656
+ /** Entity has been marked for removal */
1657
+ Removed = "removed",
1658
+ /** Entity is detached from the ORM context */
1659
+ Detached = "detached"
1660
+ }
1661
+ /**
1662
+ * Represents an entity being tracked by the ORM
1663
+ */
1664
+ interface TrackedEntity {
1665
+ /** The table definition this entity belongs to */
1666
+ table: TableDef;
1667
+ /** The actual entity instance */
1668
+ entity: any;
1669
+ /** Primary key value of the entity */
1670
+ pk: string | number | null;
1671
+ /** Current status of the entity */
1672
+ status: EntityStatus;
1673
+ /** Original values of the entity when it was loaded */
1674
+ original: Record<string, any> | null;
1675
+ }
1676
+ /**
1677
+ * Type representing a key for relation navigation
1678
+ */
1679
+ type RelationKey = string;
1680
+ /**
1681
+ * Represents a change operation on a relation
1682
+ * @typeParam T - Type of the related entity
1683
+ */
1684
+ type RelationChange<T> = {
1685
+ kind: 'add';
1686
+ entity: T;
1687
+ } | {
1688
+ kind: 'attach';
1689
+ entity: T;
1690
+ } | {
1691
+ kind: 'remove';
1692
+ entity: T;
1693
+ } | {
1694
+ kind: 'detach';
1695
+ entity: T;
1696
+ };
1697
+ /**
1698
+ * Represents a relation change entry in the unit of work
1699
+ */
1700
+ interface RelationChangeEntry {
1701
+ /** Root entity that owns the relation */
1702
+ root: any;
1703
+ /** Key of the relation being changed */
1704
+ relationKey: RelationKey;
1705
+ /** Table definition of the root entity */
1706
+ rootTable: TableDef;
1707
+ /** Name of the relation */
1708
+ relationName: string;
1709
+ /** Relation definition */
1710
+ relation: RelationDef;
1711
+ /** The change being applied */
1712
+ change: RelationChange<any>;
1713
+ }
1714
+ /**
1715
+ * Represents a domain event that can be emitted by entities
1716
+ * @typeParam TType - Type of the event (string literal)
1717
+ */
1718
+ interface DomainEvent<TType extends string = string> {
1719
+ /** Type identifier for the event */
1720
+ readonly type: TType;
1721
+ /** Timestamp when the event occurred */
1722
+ readonly occurredAt?: Date;
1723
+ }
1724
+ /**
1725
+ * Type representing any domain event
1726
+ */
1727
+ type AnyDomainEvent = DomainEvent<string>;
1728
+ /**
1729
+ * Type representing ORM-specific domain events
1730
+ */
1731
+ type OrmDomainEvent = AnyDomainEvent;
1732
+ /**
1733
+ * Interface for entities that can emit domain events
1734
+ * @typeParam E - Type of domain events this entity can emit
1735
+ */
1736
+ interface HasDomainEvents<E extends DomainEvent = AnyDomainEvent> {
1737
+ /** Array of domain events emitted by this entity */
1738
+ domainEvents?: E[];
1739
+ }
1740
+
1590
1741
  /**
1591
1742
  * Strategy interface for converting database names to TypeScript identifiers
1592
1743
  */
@@ -1596,7 +1747,7 @@ interface NamingStrategy {
1596
1747
  * @param table - Table node, function table node, or name
1597
1748
  * @returns Valid TypeScript identifier
1598
1749
  */
1599
- tableToSymbol(table: TableNode | FunctionTableNode | string): string;
1750
+ tableToSymbol(table: TableSourceNode | string): string;
1600
1751
  /**
1601
1752
  * Converts a column reference to a property name
1602
1753
  * @param column - Column node
@@ -1616,7 +1767,7 @@ declare class InterceptorPipeline {
1616
1767
  run(ctx: QueryContext, executor: DbExecutor): Promise<QueryResult[]>;
1617
1768
  }
1618
1769
 
1619
- interface OrmOptions {
1770
+ interface OrmOptions<E extends DomainEvent = OrmDomainEvent> {
1620
1771
  dialect: Dialect;
1621
1772
  executorFactory: DbExecutorFactory;
1622
1773
  interceptors?: InterceptorPipeline;
@@ -1630,56 +1781,16 @@ interface DbExecutorFactory {
1630
1781
  }
1631
1782
  interface ExternalTransaction {
1632
1783
  }
1633
- declare class Orm {
1784
+ declare class Orm<E extends DomainEvent = OrmDomainEvent> {
1634
1785
  readonly dialect: Dialect;
1635
1786
  readonly interceptors: InterceptorPipeline;
1636
1787
  readonly namingStrategy: NamingStrategy;
1637
1788
  private readonly executorFactory;
1638
- constructor(opts: OrmOptions);
1789
+ constructor(opts: OrmOptions<E>);
1639
1790
  createSession(options?: {
1640
1791
  tx?: ExternalTransaction;
1641
- }): OrmSession;
1642
- transaction<T>(fn: (session: OrmSession) => Promise<T>): Promise<T>;
1643
- }
1644
-
1645
- declare enum EntityStatus {
1646
- New = "new",
1647
- Managed = "managed",
1648
- Dirty = "dirty",
1649
- Removed = "removed",
1650
- Detached = "detached"
1651
- }
1652
- interface TrackedEntity {
1653
- table: TableDef;
1654
- entity: any;
1655
- pk: string | number | null;
1656
- status: EntityStatus;
1657
- original: Record<string, any> | null;
1658
- }
1659
- type RelationKey = string;
1660
- type RelationChange<T> = {
1661
- kind: 'add';
1662
- entity: T;
1663
- } | {
1664
- kind: 'attach';
1665
- entity: T;
1666
- } | {
1667
- kind: 'remove';
1668
- entity: T;
1669
- } | {
1670
- kind: 'detach';
1671
- entity: T;
1672
- };
1673
- interface RelationChangeEntry {
1674
- root: any;
1675
- relationKey: RelationKey;
1676
- rootTable: TableDef;
1677
- relationName: string;
1678
- relation: RelationDef;
1679
- change: RelationChange<any>;
1680
- }
1681
- interface HasDomainEvents {
1682
- domainEvents?: any[];
1792
+ }): OrmSession<E>;
1793
+ transaction<T>(fn: (session: OrmSession<E>) => Promise<T>): Promise<T>;
1683
1794
  }
1684
1795
 
1685
1796
  declare class IdentityMap {
@@ -1727,15 +1838,21 @@ declare class UnitOfWork {
1727
1838
  private getPrimaryKeyValue;
1728
1839
  }
1729
1840
 
1730
- type DomainEventHandler<Context> = (event: any, ctx: Context) => Promise<void> | void;
1731
- declare class DomainEventBus<Context> {
1841
+ type EventOfType<E extends DomainEvent, TType extends E['type']> = Extract<E, {
1842
+ type: TType;
1843
+ }>;
1844
+ type DomainEventHandler<E extends DomainEvent, Context> = (event: E, ctx: Context) => Promise<void> | void;
1845
+ type InitialHandlers<E extends DomainEvent, Context> = {
1846
+ [K in E['type']]?: DomainEventHandler<EventOfType<E, K>, Context>[];
1847
+ };
1848
+ declare class DomainEventBus<E extends DomainEvent, Context> {
1732
1849
  private readonly handlers;
1733
- constructor(initialHandlers?: Record<string, DomainEventHandler<Context>[]>);
1734
- register(name: string, handler: DomainEventHandler<Context>): void;
1850
+ constructor(initialHandlers?: InitialHandlers<E, Context>);
1851
+ on<TType extends E['type']>(type: TType, handler: DomainEventHandler<EventOfType<E, TType>, Context>): void;
1852
+ register<TType extends E['type']>(type: TType, handler: DomainEventHandler<EventOfType<E, TType>, Context>): void;
1735
1853
  dispatch(trackedEntities: Iterable<TrackedEntity>, ctx: Context): Promise<void>;
1736
- private getEventName;
1737
1854
  }
1738
- declare const addDomainEvent: (entity: HasDomainEvents, event: any) => void;
1855
+ declare const addDomainEvent: <E extends DomainEvent>(entity: HasDomainEvents<E>, event: E) => void;
1739
1856
 
1740
1857
  declare class RelationChangeProcessor {
1741
1858
  private readonly unitOfWork;
@@ -1759,16 +1876,37 @@ declare class RelationChangeProcessor {
1759
1876
  private resolvePrimaryKeyValue;
1760
1877
  }
1761
1878
 
1879
+ /**
1880
+ * Represents a single SQL query log entry
1881
+ */
1762
1882
  interface QueryLogEntry {
1883
+ /** The SQL query that was executed */
1763
1884
  sql: string;
1885
+ /** Parameters used in the query */
1764
1886
  params?: unknown[];
1765
1887
  }
1888
+ /**
1889
+ * Function type for query logging callbacks
1890
+ * @param entry - The query log entry to process
1891
+ */
1766
1892
  type QueryLogger = (entry: QueryLogEntry) => void;
1893
+ /**
1894
+ * Creates a wrapped database executor that logs all SQL queries
1895
+ * @param executor - Original database executor to wrap
1896
+ * @param logger - Optional logger function to receive query log entries
1897
+ * @returns Wrapped executor that logs queries before execution
1898
+ */
1767
1899
  declare const createQueryLoggingExecutor: (executor: DbExecutor, logger?: QueryLogger) => DbExecutor;
1768
1900
 
1901
+ /**
1902
+ * Context for SQL query execution
1903
+ */
1769
1904
  interface ExecutionContext {
1905
+ /** Database dialect to use for SQL generation */
1770
1906
  dialect: Dialect;
1907
+ /** Database executor for running SQL queries */
1771
1908
  executor: DbExecutor;
1909
+ /** Interceptor pipeline for query processing */
1772
1910
  interceptors: InterceptorPipeline;
1773
1911
  }
1774
1912
 
@@ -1785,10 +1923,10 @@ interface EntityContext {
1785
1923
  registerRelationChange(root: any, relationKey: RelationKey, rootTable: TableDef, relationName: string, relation: RelationDef, change: RelationChange<any>): void;
1786
1924
  }
1787
1925
 
1788
- interface HydrationContext {
1926
+ interface HydrationContext<E extends DomainEvent = AnyDomainEvent> {
1789
1927
  identityMap: IdentityMap;
1790
1928
  unitOfWork: UnitOfWork;
1791
- domainEvents: DomainEventBus<any>;
1929
+ domainEvents: DomainEventBus<E, OrmSession<E>>;
1792
1930
  relationChanges: RelationChangeProcessor;
1793
1931
  entityContext: EntityContext;
1794
1932
  }
@@ -1797,22 +1935,22 @@ interface OrmInterceptor {
1797
1935
  beforeFlush?(ctx: EntityContext): Promise<void> | void;
1798
1936
  afterFlush?(ctx: EntityContext): Promise<void> | void;
1799
1937
  }
1800
- interface OrmSessionOptions {
1801
- orm: Orm;
1938
+ interface OrmSessionOptions<E extends DomainEvent = OrmDomainEvent> {
1939
+ orm: Orm<E>;
1802
1940
  executor: DbExecutor;
1803
1941
  queryLogger?: QueryLogger;
1804
1942
  interceptors?: OrmInterceptor[];
1805
- domainEventHandlers?: Record<string, DomainEventHandler<OrmSession>[]>;
1943
+ domainEventHandlers?: InitialHandlers<E, OrmSession<E>>;
1806
1944
  }
1807
- declare class OrmSession implements EntityContext {
1808
- readonly orm: Orm;
1945
+ declare class OrmSession<E extends DomainEvent = OrmDomainEvent> implements EntityContext {
1946
+ readonly orm: Orm<E>;
1809
1947
  readonly executor: DbExecutor;
1810
1948
  readonly identityMap: IdentityMap;
1811
1949
  readonly unitOfWork: UnitOfWork;
1812
- readonly domainEvents: DomainEventBus<OrmSession>;
1950
+ readonly domainEvents: DomainEventBus<E, OrmSession<E>>;
1813
1951
  readonly relationChanges: RelationChangeProcessor;
1814
1952
  private readonly interceptors;
1815
- constructor(opts: OrmSessionOptions);
1953
+ constructor(opts: OrmSessionOptions<E>);
1816
1954
  get dialect(): Dialect;
1817
1955
  get identityBuckets(): Map<string, Map<string, TrackedEntity>>;
1818
1956
  get tracked(): TrackedEntity[];
@@ -1825,7 +1963,9 @@ declare class OrmSession implements EntityContext {
1825
1963
  registerRelationChange: (root: any, relationKey: RelationKey, rootTable: TableDef, relationName: string, relation: RelationDef, change: RelationChange<any>) => void;
1826
1964
  getEntitiesForTable(table: TableDef): TrackedEntity[];
1827
1965
  registerInterceptor(interceptor: OrmInterceptor): void;
1828
- registerDomainEventHandler(name: string, handler: DomainEventHandler<OrmSession>): void;
1966
+ registerDomainEventHandler<TType extends E['type']>(type: TType, handler: DomainEventHandler<Extract<E, {
1967
+ type: TType;
1968
+ }>, OrmSession<E>>): void;
1829
1969
  find<TTable extends TableDef>(entityClass: EntityConstructor, id: any): Promise<Entity<TTable> | null>;
1830
1970
  findOne<TTable extends TableDef>(qb: SelectQueryBuilder<any, TTable>): Promise<Entity<TTable> | null>;
1831
1971
  findMany<TTable extends TableDef>(qb: SelectQueryBuilder<any, TTable>): Promise<Entity<TTable>[]>;
@@ -1835,11 +1975,21 @@ declare class OrmSession implements EntityContext {
1835
1975
  commit(): Promise<void>;
1836
1976
  rollback(): Promise<void>;
1837
1977
  getExecutionContext(): ExecutionContext;
1838
- getHydrationContext(): HydrationContext;
1978
+ getHydrationContext(): HydrationContext<E>;
1839
1979
  }
1840
1980
 
1841
1981
  type SelectDialectInput = Dialect | DialectKey;
1842
1982
 
1983
+ type ColumnSelectionValue = ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode;
1984
+ type DeepSelectConfig<TTable extends TableDef> = {
1985
+ root?: (keyof TTable['columns'] & string)[];
1986
+ } & {
1987
+ [K in keyof TTable['relations'] & string]?: (keyof RelationTargetTable<TTable['relations'][K]>['columns'] & string)[];
1988
+ };
1989
+ type WhereHasOptions = {
1990
+ correlate?: ExpressionNode;
1991
+ };
1992
+ type RelationCallback = <TChildTable extends TableDef>(qb: SelectQueryBuilder<any, TChildTable>) => SelectQueryBuilder<any, TChildTable>;
1843
1993
  /**
1844
1994
 
1845
1995
  * Main query builder class for constructing SQL SELECT queries
@@ -1870,7 +2020,13 @@ declare class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
1870
2020
  */
1871
2021
  constructor(table: TTable, state?: SelectQueryState, hydration?: HydrationManager, dependencies?: Partial<SelectQueryBuilderDependencies>, lazyRelations?: Set<string>);
1872
2022
  private clone;
2023
+ /**
2024
+ * Applies an alias to the root FROM table.
2025
+ * @param alias - Alias to apply
2026
+ */
2027
+ as(alias: string): SelectQueryBuilder<T, TTable>;
1873
2028
  private resolveQueryNode;
2029
+ private applyCorrelation;
1874
2030
  private createChildBuilder;
1875
2031
  private applyAst;
1876
2032
  private applyJoin;
@@ -1884,7 +2040,12 @@ declare class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
1884
2040
  * @returns New query builder instance with selected columns
1885
2041
 
1886
2042
  */
1887
- select(columns: Record<string, ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode>): SelectQueryBuilder<T, TTable>;
2043
+ select(columns: Record<string, ColumnSelectionValue>): SelectQueryBuilder<T, TTable>;
2044
+ /**
2045
+ * Selects columns from the root table by name (typed).
2046
+ * @param cols - Column names on the root table
2047
+ */
2048
+ selectColumns<K extends keyof TTable['columns'] & string>(...cols: K[]): SelectQueryBuilder<T, TTable>;
1888
2049
  /**
1889
2050
 
1890
2051
  * Selects raw column expressions
@@ -1923,6 +2084,14 @@ declare class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
1923
2084
 
1924
2085
  */
1925
2086
  withRecursive(name: string, query: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode, columns?: string[]): SelectQueryBuilder<T, TTable>;
2087
+ /**
2088
+ * Replaces the FROM clause with a derived table (subquery with alias)
2089
+ * @param subquery - Subquery to use as the FROM source
2090
+ * @param alias - Alias for the derived table
2091
+ * @param columnAliases - Optional column alias list
2092
+ * @returns New query builder instance with updated FROM
2093
+ */
2094
+ fromSubquery(subquery: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode, alias: string, columnAliases?: string[]): SelectQueryBuilder<T, TTable>;
1926
2095
  /**
1927
2096
 
1928
2097
  * Selects a subquery as a column
@@ -1935,6 +2104,16 @@ declare class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
1935
2104
 
1936
2105
  */
1937
2106
  selectSubquery(alias: string, sub: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable>;
2107
+ /**
2108
+ * Adds a JOIN against a derived table (subquery with alias)
2109
+ * @param subquery - Subquery to join
2110
+ * @param alias - Alias for the derived table
2111
+ * @param condition - Join condition expression
2112
+ * @param joinKind - Join kind (defaults to INNER)
2113
+ * @param columnAliases - Optional column alias list for the derived table
2114
+ * @returns New query builder instance with the derived-table join
2115
+ */
2116
+ joinSubquery(subquery: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode, alias: string, condition: BinaryExpressionNode, joinKind?: JoinKind, columnAliases?: string[]): SelectQueryBuilder<T, TTable>;
1938
2117
  /**
1939
2118
 
1940
2119
  * Adds an INNER JOIN to the query
@@ -2010,6 +2189,18 @@ declare class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
2010
2189
  */
2011
2190
  include(relationName: string, options?: RelationIncludeOptions): SelectQueryBuilder<T, TTable>;
2012
2191
  includeLazy<K extends keyof RelationMap<TTable>>(relationName: K): SelectQueryBuilder<T, TTable>;
2192
+ /**
2193
+ * Selects columns for a related table in a single hop.
2194
+ */
2195
+ selectRelationColumns<K extends keyof TTable['relations'] & string, TRel extends RelationDef = TTable['relations'][K], TTarget extends TableDef = RelationTargetTable<TRel>, C extends keyof TTarget['columns'] & string = keyof TTarget['columns'] & string>(relationName: K, ...cols: C[]): SelectQueryBuilder<T, TTable>;
2196
+ /**
2197
+ * Convenience alias for selecting specific columns from a relation.
2198
+ */
2199
+ includePick<K extends keyof TTable['relations'] & string, TRel extends RelationDef = TTable['relations'][K], TTarget extends TableDef = RelationTargetTable<TRel>, C extends keyof TTarget['columns'] & string = keyof TTarget['columns'] & string>(relationName: K, cols: C[]): SelectQueryBuilder<T, TTable>;
2200
+ /**
2201
+ * Selects columns for the root table and relations from a single config object.
2202
+ */
2203
+ selectColumnsDeep(config: DeepSelectConfig<TTable>): SelectQueryBuilder<T, TTable>;
2013
2204
  getLazyRelations(): (keyof RelationMap<TTable>)[];
2014
2205
  getTable(): TTable;
2015
2206
  execute(ctx: OrmSession): Promise<Entity<TTable>[]>;
@@ -2135,7 +2326,7 @@ declare class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
2135
2326
  * @returns New query builder instance with the WHERE EXISTS condition
2136
2327
 
2137
2328
  */
2138
- whereExists(subquery: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable>;
2329
+ whereExists(subquery: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode, correlate?: ExpressionNode): SelectQueryBuilder<T, TTable>;
2139
2330
  /**
2140
2331
 
2141
2332
  * Adds a WHERE NOT EXISTS condition to the query
@@ -2145,7 +2336,7 @@ declare class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
2145
2336
  * @returns New query builder instance with the WHERE NOT EXISTS condition
2146
2337
 
2147
2338
  */
2148
- whereNotExists(subquery: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode): SelectQueryBuilder<T, TTable>;
2339
+ whereNotExists(subquery: SelectQueryBuilder<any, TableDef<any>> | SelectQueryNode, correlate?: ExpressionNode): SelectQueryBuilder<T, TTable>;
2149
2340
  /**
2150
2341
 
2151
2342
  * Adds a WHERE EXISTS condition based on a relationship
@@ -2157,7 +2348,7 @@ declare class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
2157
2348
  * @returns New query builder instance with the relationship existence check
2158
2349
 
2159
2350
  */
2160
- whereHas(relationName: string, callback?: <TChildTable extends TableDef>(qb: SelectQueryBuilder<any, TChildTable>) => SelectQueryBuilder<any, TChildTable>): SelectQueryBuilder<T, TTable>;
2351
+ whereHas(relationName: string, callbackOrOptions?: RelationCallback | WhereHasOptions, maybeOptions?: WhereHasOptions): SelectQueryBuilder<T, TTable>;
2161
2352
  /**
2162
2353
 
2163
2354
  * Adds a WHERE NOT EXISTS condition based on a relationship
@@ -2169,7 +2360,7 @@ declare class SelectQueryBuilder<T = any, TTable extends TableDef = TableDef> {
2169
2360
  * @returns New query builder instance with the relationship non-existence check
2170
2361
 
2171
2362
  */
2172
- whereHasNot(relationName: string, callback?: <TChildTable extends TableDef>(qb: SelectQueryBuilder<any, TChildTable>) => SelectQueryBuilder<any, TChildTable>): SelectQueryBuilder<T, TTable>;
2363
+ whereHasNot(relationName: string, callbackOrOptions?: RelationCallback | WhereHasOptions, maybeOptions?: WhereHasOptions): SelectQueryBuilder<T, TTable>;
2173
2364
  /**
2174
2365
 
2175
2366
  * Compiles the query to SQL for a specific dialect
@@ -2230,4 +2421,4 @@ declare const createColumn: (table: string, name: string) => ColumnNode;
2230
2421
  */
2231
2422
  declare const createLiteral: (val: string | number) => LiteralNode;
2232
2423
 
2233
- export { type TableHooks as $, type BelongsToRelation as A, type BinaryExpressionNode as B, type ColumnRef as C, Dialect as D, type ExpressionNode as E, type FunctionNode as F, type BelongsToManyRelation as G, type HydrationPlan as H, type InExpressionNode as I, type JsonPathNode as J, type HasManyCollection as K, type LogicalExpressionNode as L, type BelongsToReference as M, type NullExpressionNode as N, type OperandNode as O, type ManyToManyCollection as P, OrmSession as Q, type RelationMap as R, type SelectQueryNode as S, type TableRef as T, type UpdateQueryNode as U, SelectQueryBuilder as V, type WindowFunctionNode as W, type ExecutionContext as X, type HydrationContext as Y, type CheckConstraint as Z, type TableOptions as _, type ColumnNode as a, defineTable as a0, type ColumnType as a1, type ReferentialAction as a2, type RawDefaultValue as a3, type DefaultValue as a4, col as a5, RelationKinds as a6, type RelationType as a7, type CascadeMode as a8, type RelationDef as a9, EntityStatus as aA, type TrackedEntity as aB, type RelationKey as aC, type RelationChange as aD, type RelationChangeEntry as aE, type HasDomainEvents as aF, type QueryLogEntry as aG, type QueryLogger as aH, createQueryLoggingExecutor as aI, type QueryResult as aJ, rowsToQueryResult as aK, type SimpleQueryRunner as aL, createExecutorFromQueryRunner as aM, type EntityOrTableTargetResolver as aN, type EntityConstructor as aO, hasMany as aa, hasOne as ab, belongsTo as ac, belongsToMany as ad, type ColumnToTs as ae, type InferRow as af, type HasOneReference as ag, createColumn as ah, createLiteral as ai, isOperandNode as aj, isFunctionNode as ak, isCaseExpressionNode as al, isWindowFunctionNode as am, isExpressionSelectionNode as an, type HydrationPivotPlan as ao, type HydrationRelationPlan as ap, type HydrationMetadata as aq, type OrmInterceptor as ar, type OrmSessionOptions as as, type OrmOptions as at, type DbExecutorFactory as au, type ExternalTransaction as av, Orm as aw, type DomainEventHandler as ax, DomainEventBus as ay, addDomainEvent as az, type LiteralNode as b, type BetweenExpressionNode as c, type CaseExpressionNode as d, type ExistsExpressionNode as e, type OrderDirection as f, type ScalarSubqueryNode as g, type ColumnDef as h, type TableDef as i, type InsertQueryNode as j, type InsertCompiler as k, type CompiledQuery as l, type DialectKey as m, type UpdateCompiler as n, type DeleteQueryNode as o, type DeleteCompiler as p, type CompilerContext as q, type ForeignKeyReference as r, type IndexColumn as s, type IndexDef as t, type DbExecutor as u, type NamingStrategy as v, type EntityContext as w, type Entity as x, type HasManyRelation as y, type HasOneRelation as z };
2424
+ export { type HydrationContext as $, type Entity as A, type BinaryExpressionNode as B, type ColumnRef as C, Dialect as D, type ExpressionNode as E, type FunctionNode as F, type HasManyRelation as G, type HydrationPlan as H, type InExpressionNode as I, type JsonPathNode as J, type HasOneRelation as K, type LiteralNode as L, type BelongsToRelation as M, type NullExpressionNode as N, type OperandNode as O, type BelongsToManyRelation as P, type HasManyCollection as Q, type RelationMap as R, type SelectQueryNode as S, type TableRef as T, type UpdateQueryNode as U, type BelongsToReference as V, type WindowFunctionNode as W, type ManyToManyCollection as X, OrmSession as Y, SelectQueryBuilder as Z, type ExecutionContext as _, type ColumnNode as a, type CheckConstraint as a0, type TableOptions as a1, type TableHooks as a2, defineTable as a3, type ColumnType as a4, type ReferentialAction as a5, type RawDefaultValue as a6, type DefaultValue as a7, col as a8, RelationKinds as a9, Orm as aA, type DomainEventHandler as aB, type InitialHandlers as aC, DomainEventBus as aD, addDomainEvent as aE, EntityStatus as aF, type TrackedEntity as aG, type RelationKey as aH, type RelationChange as aI, type RelationChangeEntry as aJ, type DomainEvent as aK, type AnyDomainEvent as aL, type OrmDomainEvent as aM, type HasDomainEvents as aN, type QueryLogEntry as aO, type QueryLogger as aP, createQueryLoggingExecutor as aQ, type QueryResult as aR, rowsToQueryResult as aS, type SimpleQueryRunner as aT, createExecutorFromQueryRunner as aU, type EntityOrTableTargetResolver as aV, type EntityConstructor as aW, type RelationType as aa, type CascadeMode as ab, type RelationDef as ac, hasMany as ad, hasOne as ae, belongsTo as af, belongsToMany as ag, type RelationTargetTable as ah, type ColumnToTs as ai, type InferRow as aj, type HasOneReference as ak, createColumn as al, createLiteral as am, isOperandNode as an, isFunctionNode as ao, isCaseExpressionNode as ap, isWindowFunctionNode as aq, isExpressionSelectionNode as ar, type HydrationPivotPlan as as, type HydrationRelationPlan as at, type HydrationMetadata as au, type OrmInterceptor as av, type OrmSessionOptions as aw, type OrmOptions as ax, type DbExecutorFactory as ay, type ExternalTransaction as az, type LogicalExpressionNode as b, type BetweenExpressionNode as c, type CaseExpressionNode as d, type ExistsExpressionNode as e, type OrderDirection as f, type ScalarSubqueryNode as g, type ColumnDef as h, type TableDef as i, type InsertQueryNode as j, type InsertCompiler as k, type CompiledQuery as l, type DialectKey as m, type UpdateCompiler as n, type DeleteQueryNode as o, type DeleteCompiler as p, type CompilerContext as q, type FunctionTableNode as r, type DerivedTableNode as s, type TableSourceNode as t, type ForeignKeyReference as u, type IndexColumn as v, type IndexDef as w, type DbExecutor as x, type NamingStrategy as y, type EntityContext as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metal-orm",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -46,6 +46,10 @@
46
46
  },
47
47
  "devDependencies": {
48
48
  "@vitest/ui": "^4.0.14",
49
+ "mysql2": "^3.15.3",
50
+ "pg": "^8.16.3",
51
+ "sqlite3": "^5.1.7",
52
+ "tedious": "^19.1.3",
49
53
  "tsup": "^8.0.0",
50
54
  "typescript": "^5.5.0",
51
55
  "vitest": "^4.0.14"
@@ -1,4 +1,4 @@
1
- import type { TableNode, FunctionTableNode } from '../core/ast/query.js';
1
+ import type { TableNode, FunctionTableNode, DerivedTableNode, TableSourceNode } from '../core/ast/query.js';
2
2
  import type { ColumnNode } from '../core/ast/expression.js';
3
3
 
4
4
  /**
@@ -7,10 +7,10 @@ import type { ColumnNode } from '../core/ast/expression.js';
7
7
  export interface NamingStrategy {
8
8
  /**
9
9
  * Converts a table name to a TypeScript symbol name
10
- * @param table - Table node, function table node, or name
11
- * @returns Valid TypeScript identifier
12
- */
13
- tableToSymbol(table: TableNode | FunctionTableNode | string): string;
10
+ * @param table - Table node, function table node, or name
11
+ * @returns Valid TypeScript identifier
12
+ */
13
+ tableToSymbol(table: TableSourceNode | string): string;
14
14
 
15
15
  /**
16
16
  * Converts a column reference to a property name
@@ -27,11 +27,16 @@ export interface NamingStrategy {
27
27
  export class DefaultNamingStrategy implements NamingStrategy {
28
28
  /**
29
29
  * Converts table names to TypeScript symbols
30
- * @param table - Table node, function table node, or string name
31
- * @returns Capitalized table name (handles schema-qualified names)
32
- */
33
- tableToSymbol(table: TableNode | FunctionTableNode | string): string {
34
- const tableName = typeof table === 'string' ? table : table.name;
30
+ * @param table - Table node, function table node, or string name
31
+ * @returns Capitalized table name (handles schema-qualified names)
32
+ */
33
+ tableToSymbol(table: TableNode | FunctionTableNode | DerivedTableNode | string): string {
34
+ const tableName =
35
+ typeof table === 'string'
36
+ ? table
37
+ : table.type === 'DerivedTable'
38
+ ? table.alias
39
+ : table.name;
35
40
 
36
41
  // Handle schema-qualified names (e.g., "auth.user" → "AuthUser")
37
42
  if (tableName.includes('.')) {