metal-orm 1.1.22 → 1.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -74,6 +74,8 @@ __export(index_exports, {
74
74
  DefaultMorphToReference: () => DefaultMorphToReference,
75
75
  DefaultTypeStrategy: () => DefaultTypeStrategy,
76
76
  DeleteQueryBuilder: () => DeleteQueryBuilder,
77
+ DialectBase: () => DialectBase,
78
+ DialectFactory: () => DialectFactory,
77
79
  DomainEventBus: () => DomainEventBus,
78
80
  Email: () => Email,
79
81
  Entity: () => Entity,
@@ -301,6 +303,7 @@ __export(index_exports, {
301
303
  isNull: () => isNull,
302
304
  isNullableColumn: () => isNullableColumn,
303
305
  isOperandNode: () => isOperandNode,
306
+ isProcedureCompiler: () => isProcedureCompiler,
304
307
  isSingleTargetRelation: () => isSingleTargetRelation,
305
308
  isTableDef: () => isTableDef2,
306
309
  isTreeConfig: () => isTreeConfig,
@@ -398,6 +401,8 @@ __export(index_exports, {
398
401
  repeat: () => repeat,
399
402
  replace: () => replace,
400
403
  replaceWithRefs: () => replaceWithRefs,
404
+ requireProcedureCompiler: () => requireProcedureCompiler,
405
+ resolveDialectInput: () => resolveDialectInput,
401
406
  resolveTreeConfig: () => resolveTreeConfig,
402
407
  resolveValidator: () => resolveValidator,
403
408
  responseToRef: () => responseToRef,
@@ -1755,268 +1760,40 @@ var StandardTableFunctionStrategy = class {
1755
1760
  }
1756
1761
  };
1757
1762
 
1758
- // src/core/dialect/abstract.ts
1759
- var Dialect = class _Dialect {
1760
- /**
1761
- * Compiles a SELECT query AST to SQL
1762
- * @param ast - Query AST to compile
1763
- * @returns Compiled query with SQL and parameters
1764
- */
1765
- compileSelect(ast) {
1766
- const ctx = this.createCompilerContext();
1767
- const normalized = this.normalizeSelectAst(ast);
1768
- const rawSql = this.compileSelectAst(normalized, ctx).trim();
1769
- const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
1770
- return {
1771
- sql,
1772
- params: [...ctx.params]
1773
- };
1774
- }
1775
- compileInsert(ast) {
1776
- const ctx = this.createCompilerContext();
1777
- const rawSql = this.compileInsertAst(ast, ctx).trim();
1778
- const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
1779
- return {
1780
- sql,
1781
- params: [...ctx.params]
1782
- };
1783
- }
1784
- compileUpdate(ast) {
1785
- const ctx = this.createCompilerContext();
1786
- const rawSql = this.compileUpdateAst(ast, ctx).trim();
1787
- const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
1788
- return {
1789
- sql,
1790
- params: [...ctx.params]
1791
- };
1792
- }
1793
- compileDelete(ast) {
1794
- const ctx = this.createCompilerContext();
1795
- const rawSql = this.compileDeleteAst(ast, ctx).trim();
1796
- const sql = rawSql.endsWith(";") ? rawSql : `${rawSql};`;
1797
- return {
1798
- sql,
1799
- params: [...ctx.params]
1800
- };
1801
- }
1802
- supportsDmlReturningClause() {
1803
- return false;
1804
- }
1805
- /**
1806
- * Compiles a WHERE clause
1807
- * @param where - WHERE expression
1808
- * @param ctx - Compiler context
1809
- * @returns SQL WHERE clause or empty string
1810
- */
1811
- compileWhere(where, ctx) {
1812
- if (!where) return "";
1813
- return ` WHERE ${this.compileExpression(where, ctx)}`;
1814
- }
1815
- compileReturning(returning, _ctx) {
1816
- void _ctx;
1817
- if (!returning || returning.length === 0) return "";
1818
- throw new Error("RETURNING is not supported by this dialect.");
1819
- }
1820
- /**
1821
- * Generates subquery for EXISTS expressions
1822
- * Rule: Always forces SELECT 1, ignoring column list
1823
- * Maintains FROM, JOINs, WHERE, GROUP BY, ORDER BY, LIMIT/OFFSET
1824
- * Does not add ';' at the end
1825
- * @param ast - Query AST
1826
- * @param ctx - Compiler context
1827
- * @returns SQL for EXISTS subquery
1828
- */
1829
- compileSelectForExists(ast, ctx) {
1830
- const normalized = this.normalizeSelectAst(ast);
1831
- const full = this.compileSelectAst(normalized, ctx).trim().replace(/;$/, "");
1832
- if (normalized.setOps && normalized.setOps.length > 0) {
1833
- return `SELECT 1 FROM (${full}) AS _exists`;
1834
- }
1835
- const upper2 = full.toUpperCase();
1836
- const fromIndex = upper2.indexOf(" FROM ");
1837
- if (fromIndex === -1) {
1838
- return full;
1839
- }
1840
- const tail = full.slice(fromIndex);
1841
- return `SELECT 1${tail}`;
1842
- }
1843
- /**
1844
- * Creates a new compiler context
1845
- * @returns Compiler context with parameter management
1846
- */
1847
- createCompilerContext() {
1848
- const params = [];
1849
- let counter = 0;
1850
- return {
1851
- params,
1852
- addParameter: (value) => {
1853
- counter += 1;
1854
- params.push(value);
1855
- return this.formatPlaceholder(counter);
1856
- }
1857
- };
1858
- }
1859
- /**
1860
- * Formats a parameter placeholder
1861
- * @param index - Parameter index
1862
- * @returns Formatted placeholder string
1863
- */
1864
- formatPlaceholder(_index) {
1865
- void _index;
1866
- return "?";
1867
- }
1868
- /**
1869
- * Whether the current dialect supports a given set operation.
1870
- * Override in concrete dialects to restrict support.
1871
- */
1872
- supportsSetOperation(_kind) {
1873
- void _kind;
1874
- return true;
1875
- }
1876
- /**
1877
- * Validates set-operation semantics:
1878
- * - Ensures the dialect supports requested operators.
1879
- * - Enforces that only the outermost compound query may have ORDER/LIMIT/OFFSET.
1880
- * @param ast - Query to validate
1881
- * @param isOutermost - Whether this node is the outermost compound query
1882
- */
1883
- validateSetOperations(ast, isOutermost = true) {
1884
- const hasSetOps = !!(ast.setOps && ast.setOps.length);
1885
- if (!isOutermost && (ast.orderBy || ast.limit !== void 0 || ast.offset !== void 0)) {
1886
- throw new Error("ORDER BY / LIMIT / OFFSET are only allowed on the outermost compound query.");
1887
- }
1888
- if (hasSetOps) {
1889
- for (const op of ast.setOps) {
1890
- if (!this.supportsSetOperation(op.operator)) {
1891
- throw new Error(`Set operation ${op.operator} is not supported by this dialect.`);
1892
- }
1893
- this.validateSetOperations(op.query, false);
1894
- }
1895
- }
1896
- }
1897
- /**
1898
- * Hoists CTEs from set-operation operands to the outermost query so WITH appears once.
1899
- * @param ast - Query AST
1900
- * @returns Normalized AST without inner CTEs and a list of hoisted CTEs
1901
- */
1902
- hoistCtes(ast) {
1903
- let hoisted = [];
1904
- const normalizedSetOps = ast.setOps?.map((op) => {
1905
- const { normalized: child, hoistedCtes: childHoisted } = this.hoistCtes(op.query);
1906
- const childCtes = child.ctes ?? [];
1907
- if (childCtes.length) {
1908
- hoisted = hoisted.concat(childCtes);
1909
- }
1910
- hoisted = hoisted.concat(childHoisted);
1911
- const queryWithoutCtes = childCtes.length ? { ...child, ctes: void 0 } : child;
1912
- return { ...op, query: queryWithoutCtes };
1913
- });
1914
- const normalized = normalizedSetOps ? { ...ast, setOps: normalizedSetOps } : ast;
1915
- return { normalized, hoistedCtes: hoisted };
1916
- }
1917
- /**
1918
- * Normalizes a SELECT AST before compilation (validation + CTE hoisting).
1919
- * @param ast - Query AST
1920
- * @returns Normalized query AST
1921
- */
1922
- normalizeSelectAst(ast) {
1923
- this.validateSetOperations(ast, true);
1924
- const { normalized, hoistedCtes } = this.hoistCtes(ast);
1925
- const combinedCtes = [...normalized.ctes ?? [], ...hoistedCtes];
1926
- return combinedCtes.length ? { ...normalized, ctes: combinedCtes } : normalized;
1927
- }
1928
- expressionCompilers;
1929
- operandCompilers;
1930
- functionStrategy;
1931
- tableFunctionStrategy;
1932
- constructor(functionStrategy, tableFunctionStrategy) {
1933
- this.expressionCompilers = /* @__PURE__ */ new Map();
1934
- this.operandCompilers = /* @__PURE__ */ new Map();
1935
- this.functionStrategy = functionStrategy || new StandardFunctionStrategy();
1936
- this.tableFunctionStrategy = tableFunctionStrategy || new StandardTableFunctionStrategy();
1763
+ // src/core/dialect/base/expression-compiler-registry.ts
1764
+ var ExpressionCompilerRegistry = class {
1765
+ constructor(host) {
1766
+ this.host = host;
1937
1767
  this.registerDefaultOperandCompilers();
1938
1768
  this.registerDefaultExpressionCompilers();
1939
1769
  }
1940
- /**
1941
- * Creates a new Dialect instance (for testing purposes)
1942
- * @param functionStrategy - Optional function strategy
1943
- * @returns New Dialect instance
1944
- */
1945
- static create(functionStrategy, tableFunctionStrategy) {
1946
- class TestDialect extends _Dialect {
1947
- dialect = "sqlite";
1948
- quoteIdentifier(id) {
1949
- return `"${id}"`;
1950
- }
1951
- compileSelectAst() {
1952
- throw new Error("Not implemented");
1953
- }
1954
- compileInsertAst() {
1955
- throw new Error("Not implemented");
1956
- }
1957
- compileUpdateAst() {
1958
- throw new Error("Not implemented");
1959
- }
1960
- compileDeleteAst() {
1961
- throw new Error("Not implemented");
1962
- }
1963
- compileProcedureCall() {
1964
- throw new Error("Not implemented");
1965
- }
1966
- }
1967
- return new TestDialect(functionStrategy, tableFunctionStrategy);
1968
- }
1969
- /**
1970
- * Registers an expression compiler for a specific node type
1971
- * @param type - Expression node type
1972
- * @param compiler - Compiler function
1973
- */
1770
+ expressionCompilers = /* @__PURE__ */ new Map();
1771
+ operandCompilers = /* @__PURE__ */ new Map();
1974
1772
  registerExpressionCompiler(type, compiler) {
1975
1773
  this.expressionCompilers.set(type, compiler);
1976
1774
  }
1977
- /**
1978
- * Registers an operand compiler for a specific node type
1979
- * @param type - Operand node type
1980
- * @param compiler - Compiler function
1981
- */
1982
1775
  registerOperandCompiler(type, compiler) {
1983
1776
  this.operandCompilers.set(type, compiler);
1984
1777
  }
1985
- /**
1986
- * Compiles an expression node
1987
- * @param node - Expression node to compile
1988
- * @param ctx - Compiler context
1989
- * @returns Compiled SQL expression
1990
- */
1991
1778
  compileExpression(node, ctx) {
1992
1779
  const compiler = this.expressionCompilers.get(node.type);
1993
1780
  if (!compiler) {
1994
- throw new Error(`Unsupported expression node type "${node.type}" for ${this.constructor.name}`);
1781
+ throw new Error(`Unsupported expression node type "${node.type}" for ${this.host.describe()}`);
1995
1782
  }
1996
1783
  return compiler(node, ctx);
1997
1784
  }
1998
- /**
1999
- * Compiles an operand node
2000
- * @param node - Operand node to compile
2001
- * @param ctx - Compiler context
2002
- * @returns Compiled SQL operand
2003
- */
2004
1785
  compileOperand(node, ctx) {
2005
1786
  const compiler = this.operandCompilers.get(node.type);
2006
1787
  if (!compiler) {
2007
- throw new Error(`Unsupported operand node type "${node.type}" for ${this.constructor.name}`);
1788
+ throw new Error(`Unsupported operand node type "${node.type}" for ${this.host.describe()}`);
2008
1789
  }
2009
1790
  return compiler(node, ctx);
2010
1791
  }
2011
- /**
2012
- * Compiles an ordering term (operand, expression, or alias reference).
2013
- */
2014
1792
  compileOrderingTerm(term, ctx) {
2015
1793
  if (isOperandNode(term)) {
2016
1794
  return this.compileOperand(term, ctx);
2017
1795
  }
2018
- const expr = this.compileExpression(term, ctx);
2019
- return `(${expr})`;
1796
+ return `(${this.compileExpression(term, ctx)})`;
2020
1797
  }
2021
1798
  registerDefaultExpressionCompilers() {
2022
1799
  this.registerExpressionCompiler("BinaryExpression", (binary, ctx) => {
@@ -2051,11 +1828,11 @@ var Dialect = class _Dialect {
2051
1828
  const values = inExpr.right.map((v) => this.compileOperand(v, ctx)).join(", ");
2052
1829
  return `${left2} ${inExpr.operator} (${values})`;
2053
1830
  }
2054
- const subquerySql = this.compileSelectAst(inExpr.right.query, ctx).trim().replace(/;$/, "");
1831
+ const subquerySql = this.host.compileSelectAst(inExpr.right.query, ctx).trim().replace(/;$/, "");
2055
1832
  return `${left2} ${inExpr.operator} (${subquerySql})`;
2056
1833
  });
2057
1834
  this.registerExpressionCompiler("ExistsExpression", (existsExpr, ctx) => {
2058
- const subquerySql = this.compileSelectForExists(existsExpr.subquery, ctx);
1835
+ const subquerySql = this.host.compileSelectForExists(existsExpr.subquery, ctx);
2059
1836
  return `${existsExpr.operator} (${subquerySql})`;
2060
1837
  });
2061
1838
  this.registerExpressionCompiler("BetweenExpression", (betweenExpr, ctx) => {
@@ -2081,25 +1858,28 @@ var Dialect = class _Dialect {
2081
1858
  });
2082
1859
  }
2083
1860
  registerDefaultOperandCompilers() {
2084
- this.registerOperandCompiler("Literal", (literal, ctx) => ctx.addParameter(literal.value));
2085
- this.registerOperandCompiler("AliasRef", (alias, _ctx) => {
2086
- void _ctx;
2087
- return this.quoteIdentifier(alias.name);
2088
- });
2089
- this.registerOperandCompiler("Column", (column, _ctx) => {
2090
- void _ctx;
2091
- return `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`;
2092
- });
1861
+ this.registerOperandCompiler(
1862
+ "Literal",
1863
+ (literal, ctx) => ctx.addParameter(literal.value)
1864
+ );
1865
+ this.registerOperandCompiler(
1866
+ "AliasRef",
1867
+ (alias) => this.host.quoteIdentifier(alias.name)
1868
+ );
1869
+ this.registerOperandCompiler(
1870
+ "Column",
1871
+ (column) => `${this.host.quoteIdentifier(column.table)}.${this.host.quoteIdentifier(column.name)}`
1872
+ );
2093
1873
  this.registerOperandCompiler(
2094
1874
  "Function",
2095
- (fnNode, ctx) => this.compileFunctionOperand(fnNode, ctx)
1875
+ (fnNode, ctx) => this.host.compileFunctionOperand(fnNode, ctx)
1876
+ );
1877
+ this.registerOperandCompiler(
1878
+ "JsonPath",
1879
+ (path) => this.host.compileJsonPath(path)
2096
1880
  );
2097
- this.registerOperandCompiler("JsonPath", (path, _ctx) => {
2098
- void _ctx;
2099
- return this.compileJsonPath(path);
2100
- });
2101
1881
  this.registerOperandCompiler("ScalarSubquery", (node, ctx) => {
2102
- const sql = this.compileSelectAst(node.query, ctx).trim().replace(/;$/, "");
1882
+ const sql = this.host.compileSelectAst(node.query, ctx).trim().replace(/;$/, "");
2103
1883
  return `(${sql})`;
2104
1884
  });
2105
1885
  this.registerOperandCompiler("CaseExpression", (node, ctx) => {
@@ -2126,7 +1906,7 @@ var Dialect = class _Dialect {
2126
1906
  const parts = [];
2127
1907
  if (node.partitionBy && node.partitionBy.length > 0) {
2128
1908
  const partitionClause = "PARTITION BY " + node.partitionBy.map(
2129
- (col2) => `${this.quoteIdentifier(col2.table)}.${this.quoteIdentifier(col2.name)}`
1909
+ (col2) => `${this.host.quoteIdentifier(col2.table)}.${this.host.quoteIdentifier(col2.name)}`
2130
1910
  ).join(", ");
2131
1911
  parts.push(partitionClause);
2132
1912
  }
@@ -2158,14 +1938,164 @@ var Dialect = class _Dialect {
2158
1938
  return `${expr} COLLATE ${node.collation}`;
2159
1939
  });
2160
1940
  }
2161
- // Default fallback, should be overridden by dialects if supported
1941
+ };
1942
+
1943
+ // src/core/dialect/base/select-ast-normalizer.ts
1944
+ var SelectAstNormalizer = class {
1945
+ constructor(supportsSetOperation) {
1946
+ this.supportsSetOperation = supportsSetOperation;
1947
+ }
1948
+ normalize(ast) {
1949
+ this.validateSetOperations(ast, true);
1950
+ const { normalized, hoistedCtes } = this.hoistCtes(ast);
1951
+ const combinedCtes = [...normalized.ctes ?? [], ...hoistedCtes];
1952
+ return combinedCtes.length ? { ...normalized, ctes: combinedCtes } : normalized;
1953
+ }
1954
+ validateSetOperations(ast, isOutermost) {
1955
+ const hasSetOps = !!(ast.setOps && ast.setOps.length);
1956
+ if (!isOutermost && (ast.orderBy || ast.limit !== void 0 || ast.offset !== void 0)) {
1957
+ throw new Error("ORDER BY / LIMIT / OFFSET are only allowed on the outermost compound query.");
1958
+ }
1959
+ if (!hasSetOps) return;
1960
+ for (const op of ast.setOps) {
1961
+ if (!this.supportsSetOperation(op.operator)) {
1962
+ throw new Error(`Set operation ${op.operator} is not supported by this dialect.`);
1963
+ }
1964
+ this.validateSetOperations(op.query, false);
1965
+ }
1966
+ }
1967
+ hoistCtes(ast) {
1968
+ let hoisted = [];
1969
+ const normalizedSetOps = ast.setOps?.map((op) => {
1970
+ const { normalized: child, hoistedCtes: childHoisted } = this.hoistCtes(op.query);
1971
+ const childCtes = child.ctes ?? [];
1972
+ if (childCtes.length) hoisted = hoisted.concat(childCtes);
1973
+ hoisted = hoisted.concat(childHoisted);
1974
+ const queryWithoutCtes = childCtes.length ? { ...child, ctes: void 0 } : child;
1975
+ return { ...op, query: queryWithoutCtes };
1976
+ });
1977
+ const normalized = normalizedSetOps ? { ...ast, setOps: normalizedSetOps } : ast;
1978
+ return { normalized, hoistedCtes: hoisted };
1979
+ }
1980
+ };
1981
+
1982
+ // src/core/dialect/abstract.ts
1983
+ var DialectBase = class _DialectBase {
1984
+ expressionCompilerRegistry;
1985
+ selectAstNormalizer;
1986
+ functionStrategy;
1987
+ tableFunctionStrategy;
1988
+ constructor(functionStrategy, tableFunctionStrategy) {
1989
+ this.functionStrategy = functionStrategy ?? new StandardFunctionStrategy();
1990
+ this.tableFunctionStrategy = tableFunctionStrategy ?? new StandardTableFunctionStrategy();
1991
+ this.selectAstNormalizer = new SelectAstNormalizer((kind) => this.supportsSetOperation(kind));
1992
+ this.expressionCompilerRegistry = new ExpressionCompilerRegistry({
1993
+ quoteIdentifier: (id) => this.quoteIdentifier(id),
1994
+ compileSelectAst: (ast, ctx) => this.compileSelectAst(ast, ctx),
1995
+ compileSelectForExists: (ast, ctx) => this.compileSelectForExists(ast, ctx),
1996
+ compileJsonPath: (node) => this.compileJsonPath(node),
1997
+ compileFunctionOperand: (node, ctx) => this.compileFunctionOperand(node, ctx),
1998
+ describe: () => this.constructor.name
1999
+ });
2000
+ }
2001
+ compileSelect(ast) {
2002
+ const ctx = this.createCompilerContext();
2003
+ const normalized = this.normalizeSelectAst(ast);
2004
+ const rawSql = this.compileSelectAst(normalized, ctx).trim();
2005
+ return {
2006
+ sql: rawSql.endsWith(";") ? rawSql : `${rawSql};`,
2007
+ params: [...ctx.params]
2008
+ };
2009
+ }
2010
+ compileInsert(ast) {
2011
+ const ctx = this.createCompilerContext();
2012
+ const rawSql = this.compileInsertAst(ast, ctx).trim();
2013
+ return {
2014
+ sql: rawSql.endsWith(";") ? rawSql : `${rawSql};`,
2015
+ params: [...ctx.params]
2016
+ };
2017
+ }
2018
+ compileUpdate(ast) {
2019
+ const ctx = this.createCompilerContext();
2020
+ const rawSql = this.compileUpdateAst(ast, ctx).trim();
2021
+ return {
2022
+ sql: rawSql.endsWith(";") ? rawSql : `${rawSql};`,
2023
+ params: [...ctx.params]
2024
+ };
2025
+ }
2026
+ compileDelete(ast) {
2027
+ const ctx = this.createCompilerContext();
2028
+ const rawSql = this.compileDeleteAst(ast, ctx).trim();
2029
+ return {
2030
+ sql: rawSql.endsWith(";") ? rawSql : `${rawSql};`,
2031
+ params: [...ctx.params]
2032
+ };
2033
+ }
2034
+ supportsDmlReturningClause() {
2035
+ return false;
2036
+ }
2037
+ compileWhere(where, ctx) {
2038
+ if (!where) return "";
2039
+ return ` WHERE ${this.compileExpression(where, ctx)}`;
2040
+ }
2041
+ compileReturning(returning, _ctx) {
2042
+ void _ctx;
2043
+ if (!returning || returning.length === 0) return "";
2044
+ throw new Error("RETURNING is not supported by this dialect.");
2045
+ }
2046
+ compileSelectForExists(ast, ctx) {
2047
+ const normalized = this.normalizeSelectAst(ast);
2048
+ const full = this.compileSelectAst(normalized, ctx).trim().replace(/;$/, "");
2049
+ if (normalized.setOps && normalized.setOps.length > 0) {
2050
+ return `SELECT 1 FROM (${full}) AS _exists`;
2051
+ }
2052
+ const upper2 = full.toUpperCase();
2053
+ const fromIndex = upper2.indexOf(" FROM ");
2054
+ if (fromIndex === -1) return full;
2055
+ return `SELECT 1${full.slice(fromIndex)}`;
2056
+ }
2057
+ createCompilerContext() {
2058
+ const params = [];
2059
+ let counter = 0;
2060
+ return {
2061
+ params,
2062
+ addParameter: (value) => {
2063
+ counter += 1;
2064
+ params.push(value);
2065
+ return this.formatPlaceholder(counter);
2066
+ }
2067
+ };
2068
+ }
2069
+ formatPlaceholder(_index) {
2070
+ void _index;
2071
+ return "?";
2072
+ }
2073
+ supportsSetOperation(_kind) {
2074
+ void _kind;
2075
+ return true;
2076
+ }
2077
+ normalizeSelectAst(ast) {
2078
+ return this.selectAstNormalizer.normalize(ast);
2079
+ }
2080
+ registerExpressionCompiler(type, compiler) {
2081
+ this.expressionCompilerRegistry.registerExpressionCompiler(type, compiler);
2082
+ }
2083
+ registerOperandCompiler(type, compiler) {
2084
+ this.expressionCompilerRegistry.registerOperandCompiler(type, compiler);
2085
+ }
2086
+ compileExpression(node, ctx) {
2087
+ return this.expressionCompilerRegistry.compileExpression(node, ctx);
2088
+ }
2089
+ compileOperand(node, ctx) {
2090
+ return this.expressionCompilerRegistry.compileOperand(node, ctx);
2091
+ }
2092
+ compileOrderingTerm(term, ctx) {
2093
+ return this.expressionCompilerRegistry.compileOrderingTerm(term, ctx);
2094
+ }
2162
2095
  compileJsonPath(_node) {
2163
2096
  void _node;
2164
2097
  throw new Error("JSON Path not supported by this dialect");
2165
2098
  }
2166
- /**
2167
- * Compiles a function operand, using the dialect's function strategy.
2168
- */
2169
2099
  compileFunctionOperand(fnNode, ctx) {
2170
2100
  const compiledArgs = fnNode.args.map((arg) => this.compileOperand(arg, ctx));
2171
2101
  const renderer = this.functionStrategy.getRenderer(fnNode.name);
@@ -2178,98 +2108,62 @@ var Dialect = class _Dialect {
2178
2108
  }
2179
2109
  return `${fnNode.name}(${compiledArgs.join(", ")})`;
2180
2110
  }
2111
+ /** Creates a minimal dialect implementation for isolated compiler tests. */
2112
+ static create(functionStrategy, tableFunctionStrategy) {
2113
+ class TestDialect extends _DialectBase {
2114
+ dialect = "sqlite";
2115
+ quoteIdentifier(id) {
2116
+ return `"${id}"`;
2117
+ }
2118
+ compileSelectAst() {
2119
+ throw new Error("Not implemented");
2120
+ }
2121
+ compileInsertAst() {
2122
+ throw new Error("Not implemented");
2123
+ }
2124
+ compileUpdateAst() {
2125
+ throw new Error("Not implemented");
2126
+ }
2127
+ compileDeleteAst() {
2128
+ throw new Error("Not implemented");
2129
+ }
2130
+ }
2131
+ return new TestDialect(functionStrategy, tableFunctionStrategy);
2132
+ }
2181
2133
  };
2182
2134
 
2183
2135
  // src/core/dialect/base/function-table-formatter.ts
2184
2136
  var FunctionTableFormatter = class {
2185
- /**
2186
- * Formats a function table node into SQL syntax.
2187
- * @param fn - The function table node containing schema, name, args, and aliases.
2188
- * @param ctx - Optional compiler context for operand compilation.
2189
- * @param dialect - The dialect instance for compiling operands.
2190
- * @returns SQL function table expression (e.g., "LATERAL schema.func(args) WITH ORDINALITY AS alias(col1, col2)").
2191
- */
2192
- static format(fn9, ctx, dialect) {
2193
- const schemaPart = this.formatSchema(fn9, dialect);
2194
- const args = this.formatArgs(fn9, ctx, dialect);
2137
+ static format(fn9, ctx, formatter) {
2138
+ const schemaPart = this.formatSchema(fn9, formatter);
2139
+ const args = this.formatArgs(fn9, ctx, formatter);
2195
2140
  const base = this.formatBase(fn9, schemaPart, args);
2196
2141
  const lateral = this.formatLateral(fn9);
2197
- const alias = this.formatAlias(fn9, dialect);
2198
- const colAliases = this.formatColumnAliases(fn9, dialect);
2142
+ const alias = this.formatAlias(fn9, formatter);
2143
+ const colAliases = this.formatColumnAliases(fn9, formatter);
2199
2144
  return `${lateral}${base}${alias}${colAliases}`;
2200
2145
  }
2201
- /**
2202
- * Formats the schema prefix for the function name.
2203
- * @param fn - The function table node.
2204
- * @param dialect - The dialect instance for quoting identifiers.
2205
- * @returns Schema prefix (e.g., "schema.") or empty string.
2206
- * @internal
2207
- */
2208
- static formatSchema(fn9, dialect) {
2146
+ static formatSchema(fn9, formatter) {
2209
2147
  if (!fn9.schema) return "";
2210
- const quoted = dialect ? dialect.quoteIdentifier(fn9.schema) : fn9.schema;
2211
- return `${quoted}.`;
2148
+ return `${formatter.quoteIdentifier(fn9.schema)}.`;
2212
2149
  }
2213
- /**
2214
- * Formats function arguments into SQL syntax.
2215
- * @param fn - The function table node containing arguments.
2216
- * @param ctx - Optional compiler context for operand compilation.
2217
- * @param dialect - The dialect instance for compiling operands.
2218
- * @returns Comma-separated function arguments.
2219
- * @internal
2220
- */
2221
- static formatArgs(fn9, ctx, dialect) {
2222
- return (fn9.args || []).map((a) => {
2223
- if (ctx && dialect) {
2224
- return dialect.compileOperand(a, ctx);
2225
- }
2226
- return String(a);
2227
- }).join(", ");
2150
+ static formatArgs(fn9, ctx, formatter) {
2151
+ return (fn9.args || []).map((arg) => ctx ? formatter.compileOperand(arg, ctx) : String(arg)).join(", ");
2228
2152
  }
2229
- /**
2230
- * Formats the base function call with WITH ORDINALITY if present.
2231
- * @param fn - The function table node.
2232
- * @param schemaPart - Formatted schema prefix.
2233
- * @param args - Formatted function arguments.
2234
- * @param dialect - The dialect instance for quoting identifiers.
2235
- * @returns Base function call expression (e.g., "schema.func(args) WITH ORDINALITY").
2236
- * @internal
2237
- */
2238
2153
  static formatBase(fn9, schemaPart, args) {
2239
2154
  const ordinality = fn9.withOrdinality ? " WITH ORDINALITY" : "";
2240
2155
  return `${schemaPart}${fn9.name}(${args})${ordinality}`;
2241
2156
  }
2242
- /**
2243
- * Formats the LATERAL keyword if present.
2244
- * @param fn - The function table node.
2245
- * @returns "LATERAL " or empty string.
2246
- * @internal
2247
- */
2248
2157
  static formatLateral(fn9) {
2249
2158
  return fn9.lateral ? "LATERAL " : "";
2250
2159
  }
2251
- /**
2252
- * Formats the table alias for the function table.
2253
- * @param fn - The function table node.
2254
- * @param dialect - The dialect instance for quoting identifiers.
2255
- * @returns " AS alias" or empty string.
2256
- * @internal
2257
- */
2258
- static formatAlias(fn9, dialect) {
2160
+ static formatAlias(fn9, formatter) {
2259
2161
  if (!fn9.alias) return "";
2260
- const quoted = dialect ? dialect.quoteIdentifier(fn9.alias) : fn9.alias;
2261
- return ` AS ${quoted}`;
2162
+ return ` AS ${formatter.quoteIdentifier(fn9.alias)}`;
2262
2163
  }
2263
- /**
2264
- * Formats column aliases for the function table result columns.
2265
- * @param fn - The function table node containing column aliases.
2266
- * @param dialect - The dialect instance for quoting identifiers.
2267
- * @returns "(col1, col2, ...)" or empty string.
2268
- * @internal
2269
- */
2270
- static formatColumnAliases(fn9, dialect) {
2164
+ static formatColumnAliases(fn9, formatter) {
2271
2165
  if (!fn9.columnAliases || !fn9.columnAliases.length) return "";
2272
- const aliases = fn9.columnAliases.map((col2) => dialect ? dialect.quoteIdentifier(col2) : col2).join(", ");
2166
+ const aliases = fn9.columnAliases.map((col2) => formatter.quoteIdentifier(col2)).join(", ");
2273
2167
  return `(${aliases})`;
2274
2168
  }
2275
2169
  };
@@ -2395,7 +2289,7 @@ var OrderByCompiler = class {
2395
2289
  };
2396
2290
 
2397
2291
  // src/core/dialect/base/sql-dialect.ts
2398
- var SqlDialectBase = class extends Dialect {
2292
+ var SqlDialectBase = class extends DialectBase {
2399
2293
  paginationStrategy = new StandardLimitOffsetPagination();
2400
2294
  returningStrategy = new NoReturningStrategy();
2401
2295
  compileSelectAst(ast, ctx) {
@@ -2405,14 +2299,12 @@ var SqlDialectBase = class extends Dialect {
2405
2299
  ctx,
2406
2300
  this.quoteIdentifier.bind(this),
2407
2301
  this.compileSelectAst.bind(this),
2408
- this.normalizeSelectAst?.bind(this) ?? ((a) => a),
2302
+ this.normalizeSelectAst.bind(this),
2409
2303
  this.stripTrailingSemicolon.bind(this)
2410
2304
  );
2411
2305
  const baseAst = hasSetOps ? { ...ast, setOps: void 0, orderBy: void 0, limit: void 0, offset: void 0 } : ast;
2412
2306
  const baseSelect = this.compileSelectCore(baseAst, ctx);
2413
- if (!hasSetOps) {
2414
- return `${ctes}${baseSelect}`;
2415
- }
2307
+ if (!hasSetOps) return `${ctes}${baseSelect}`;
2416
2308
  return this.compileSelectWithSetOps(ast, baseSelect, ctes, ctx);
2417
2309
  }
2418
2310
  compileSelectWithSetOps(ast, baseSelect, ctes, ctx) {
@@ -2461,9 +2353,7 @@ var SqlDialectBase = class extends Dialect {
2461
2353
  return columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
2462
2354
  }
2463
2355
  ensureConflictColumns(clause, message) {
2464
- if (!clause.target.columns.length) {
2465
- throw new Error(message);
2466
- }
2356
+ if (!clause.target.columns.length) throw new Error(message);
2467
2357
  }
2468
2358
  compileSelectCore(ast, ctx) {
2469
2359
  const columns = this.compileSelectColumns(ast, ctx);
@@ -2496,8 +2386,7 @@ var SqlDialectBase = class extends Dialect {
2496
2386
  }
2497
2387
  compileUpdateAssignments(assignments, table, ctx) {
2498
2388
  return assignments.map((assignment) => {
2499
- const col2 = assignment.column;
2500
- const target = this.compileSetTarget(col2, table);
2389
+ const target = this.compileSetTarget(assignment.column, table);
2501
2390
  const value = this.compileOperand(assignment.value, ctx);
2502
2391
  return `${target} = ${value}`;
2503
2392
  }).join(", ");
@@ -2510,9 +2399,7 @@ var SqlDialectBase = class extends Dialect {
2510
2399
  const alias = table.alias;
2511
2400
  const columnTable = column.table ?? alias ?? baseTableName;
2512
2401
  const tableQualifier = alias && column.table === baseTableName ? alias : columnTable;
2513
- if (!tableQualifier) {
2514
- return this.quoteIdentifier(column.name);
2515
- }
2402
+ if (!tableQualifier) return this.quoteIdentifier(column.name);
2516
2403
  return `${this.quoteIdentifier(tableQualifier)}.${this.quoteIdentifier(column.name)}`;
2517
2404
  }
2518
2405
  compileDeleteAst(ast, ctx) {
@@ -2529,27 +2416,20 @@ var SqlDialectBase = class extends Dialect {
2529
2416
  return ast.distinct ? "DISTINCT " : "";
2530
2417
  }
2531
2418
  compileSelectColumns(ast, ctx) {
2532
- if (!ast.columns || ast.columns.length === 0) {
2533
- return "*";
2534
- }
2535
- return ast.columns.map((c) => {
2536
- const expr = this.compileOperand(c, ctx);
2537
- if (c.alias) {
2538
- if (c.alias.includes("(")) return c.alias;
2539
- return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
2419
+ if (!ast.columns || ast.columns.length === 0) return "*";
2420
+ return ast.columns.map((column) => {
2421
+ const expr = this.compileOperand(column, ctx);
2422
+ if (column.alias) {
2423
+ if (column.alias.includes("(")) return column.alias;
2424
+ return `${expr} AS ${this.quoteIdentifier(column.alias)}`;
2540
2425
  }
2541
2426
  return expr;
2542
2427
  }).join(", ");
2543
2428
  }
2544
2429
  compileFrom(ast, ctx) {
2545
- const tableSource = ast;
2546
- if (tableSource.type === "FunctionTable") {
2547
- return this.compileFunctionTable(tableSource, ctx);
2548
- }
2549
- if (tableSource.type === "DerivedTable") {
2550
- return this.compileDerivedTable(tableSource, ctx);
2551
- }
2552
- return this.compileTableSource(tableSource);
2430
+ if (ast.type === "FunctionTable") return this.compileFunctionTable(ast, ctx);
2431
+ if (ast.type === "DerivedTable") return this.compileDerivedTable(ast, ctx);
2432
+ return this.compileTableSource(ast);
2553
2433
  }
2554
2434
  compileFunctionTable(fn9, ctx) {
2555
2435
  const key = fn9.key ?? fn9.name;
@@ -2568,23 +2448,20 @@ var SqlDialectBase = class extends Dialect {
2568
2448
  throw new Error(`Table function "${key}" is not supported by dialect "${this.dialect}".`);
2569
2449
  }
2570
2450
  }
2571
- return FunctionTableFormatter.format(fn9, ctx, this);
2451
+ return FunctionTableFormatter.format(fn9, ctx, {
2452
+ quoteIdentifier: (id) => this.quoteIdentifier(id),
2453
+ compileOperand: (node, compilerContext) => this.compileOperand(node, compilerContext)
2454
+ });
2572
2455
  }
2573
2456
  compileDerivedTable(table, ctx) {
2574
- if (!table.alias) {
2575
- throw new Error("Derived tables must have an alias.");
2576
- }
2457
+ if (!table.alias) throw new Error("Derived tables must have an alias.");
2577
2458
  const subquery = this.compileSelectAst(this.normalizeSelectAst(table.query), ctx).trim().replace(/;$/, "");
2578
2459
  const columns = table.columnAliases?.length ? ` (${table.columnAliases.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
2579
2460
  return `(${subquery}) AS ${this.quoteIdentifier(table.alias)}${columns}`;
2580
2461
  }
2581
2462
  compileTableSource(table) {
2582
- if (table.type === "FunctionTable") {
2583
- return this.compileFunctionTable(table);
2584
- }
2585
- if (table.type === "DerivedTable") {
2586
- return this.compileDerivedTable(table);
2587
- }
2463
+ if (table.type === "FunctionTable") return this.compileFunctionTable(table);
2464
+ if (table.type === "DerivedTable") return this.compileDerivedTable(table);
2588
2465
  const base = this.compileTableName(table);
2589
2466
  return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
2590
2467
  }
@@ -2600,9 +2477,7 @@ var SqlDialectBase = class extends Dialect {
2600
2477
  }
2601
2478
  compileUpdateFromClause(ast, ctx) {
2602
2479
  if (!ast.from && (!ast.joins || ast.joins.length === 0)) return "";
2603
- if (!ast.from) {
2604
- throw new Error("UPDATE with JOINs requires an explicit FROM clause.");
2605
- }
2480
+ if (!ast.from) throw new Error("UPDATE with JOINs requires an explicit FROM clause.");
2606
2481
  const from = this.compileFrom(ast.from, ctx);
2607
2482
  const joins = JoinCompiler.compileJoins(
2608
2483
  ast.joins,
@@ -2614,9 +2489,7 @@ var SqlDialectBase = class extends Dialect {
2614
2489
  }
2615
2490
  compileDeleteUsingClause(ast, ctx) {
2616
2491
  if (!ast.using && (!ast.joins || ast.joins.length === 0)) return "";
2617
- if (!ast.using) {
2618
- throw new Error("DELETE with JOINs requires a USING clause.");
2619
- }
2492
+ if (!ast.using) throw new Error("DELETE with JOINs requires a USING clause.");
2620
2493
  const usingTable = this.compileFrom(ast.using, ctx);
2621
2494
  const joins = JoinCompiler.compileJoins(
2622
2495
  ast.joins,
@@ -2634,8 +2507,7 @@ var SqlDialectBase = class extends Dialect {
2634
2507
  return sql.trim().replace(/;$/, "");
2635
2508
  }
2636
2509
  wrapSetOperand(sql) {
2637
- const trimmed = this.stripTrailingSemicolon(sql);
2638
- return `(${trimmed})`;
2510
+ return `(${this.stripTrailingSemicolon(sql)})`;
2639
2511
  }
2640
2512
  renderOrderByNulls(order) {
2641
2513
  return order.nulls ? ` NULLS ${order.nulls}` : "";
@@ -3344,10 +3216,6 @@ var SqliteDialect = class extends SqlDialectBase {
3344
3216
  supportsDmlReturningClause() {
3345
3217
  return true;
3346
3218
  }
3347
- compileProcedureCall(_ast) {
3348
- void _ast;
3349
- throw new Error("Stored procedures are not supported by the SQLite dialect.");
3350
- }
3351
3219
  };
3352
3220
 
3353
3221
  // src/core/dialect/mssql/functions.ts
@@ -3782,9 +3650,8 @@ var DialectFactory = class {
3782
3650
  /**
3783
3651
  * Register (or override) a dialect factory for a key.
3784
3652
  *
3785
- * Examples:
3786
- * DialectFactory.register('sqlite', () => new SqliteDialect());
3787
- * DialectFactory.register('my-tenant-dialect', () => new CustomDialect());
3653
+ * Implementations are structural: extending DialectBase/SqlDialectBase is
3654
+ * optional. A composed object satisfying Dialect is a valid registration.
3788
3655
  */
3789
3656
  static register(key, factory) {
3790
3657
  this.registry.set(key, factory);
@@ -11357,6 +11224,15 @@ var DeleteQueryBuilder = class _DeleteQueryBuilder {
11357
11224
  };
11358
11225
  var isTableSourceNode2 = (source) => typeof source.type === "string";
11359
11226
 
11227
+ // src/core/dialect/capabilities/procedure-compiler.ts
11228
+ var isProcedureCompiler = (value) => typeof value?.compileProcedureCall === "function";
11229
+ var requireProcedureCompiler = (value) => {
11230
+ if (!isProcedureCompiler(value)) {
11231
+ throw new Error("Stored procedures are not supported by this dialect.");
11232
+ }
11233
+ return value;
11234
+ };
11235
+
11360
11236
  // src/orm/execute-procedure.ts
11361
11237
  var resolveColumnIndex = (columns, expectedName) => {
11362
11238
  const exact = columns.findIndex((column) => column === expectedName);
@@ -11395,7 +11271,7 @@ var extractOutValues = (compiled, resultSets) => {
11395
11271
  };
11396
11272
  var executeProcedureAst = async (session, ast) => {
11397
11273
  const execCtx = session.getExecutionContext();
11398
- const compiled = execCtx.dialect.compileProcedureCall(ast);
11274
+ const compiled = requireProcedureCompiler(execCtx.dialect).compileProcedureCall(ast);
11399
11275
  const payload = await execCtx.interceptors.run(
11400
11276
  { sql: compiled.sql, params: compiled.params },
11401
11277
  execCtx.executor
@@ -11468,8 +11344,7 @@ var ProcedureCallBuilder = class _ProcedureCallBuilder {
11468
11344
  }
11469
11345
  compile(dialect) {
11470
11346
  const resolved = resolveDialectInput(dialect);
11471
- this.validateMssqlOutDbType(resolved);
11472
- return resolved.compileProcedureCall(this.getAST());
11347
+ return requireProcedureCompiler(resolved).compileProcedureCall(this.getAST());
11473
11348
  }
11474
11349
  toSql(dialect) {
11475
11350
  return this.compile(dialect).sql;
@@ -11482,21 +11357,8 @@ var ProcedureCallBuilder = class _ProcedureCallBuilder {
11482
11357
  };
11483
11358
  }
11484
11359
  async execute(session) {
11485
- this.validateMssqlOutDbType(session.getExecutionContext().dialect);
11486
11360
  return executeProcedureAst(session, this.getAST());
11487
11361
  }
11488
- validateMssqlOutDbType(dialect) {
11489
- const isMssqlDialect = dialect.constructor.name === "SqlServerDialect";
11490
- if (!isMssqlDialect) return;
11491
- for (const param of this.ast.params) {
11492
- const needsDbType = param.direction === "out" || param.direction === "inout";
11493
- if (needsDbType && !param.dbType) {
11494
- throw new Error(
11495
- `MSSQL requires "dbType" for procedure parameter "${param.name}" with direction "${param.direction}".`
11496
- );
11497
- }
11498
- }
11499
- }
11500
11362
  };
11501
11363
  var callProcedure = (name, options) => new ProcedureCallBuilder(name, options);
11502
11364
 
@@ -22225,6 +22087,8 @@ async function bulkUpsert(session, table, rows, options = {}) {
22225
22087
  DefaultMorphToReference,
22226
22088
  DefaultTypeStrategy,
22227
22089
  DeleteQueryBuilder,
22090
+ DialectBase,
22091
+ DialectFactory,
22228
22092
  DomainEventBus,
22229
22093
  Email,
22230
22094
  Entity,
@@ -22452,6 +22316,7 @@ async function bulkUpsert(session, table, rows, options = {}) {
22452
22316
  isNull,
22453
22317
  isNullableColumn,
22454
22318
  isOperandNode,
22319
+ isProcedureCompiler,
22455
22320
  isSingleTargetRelation,
22456
22321
  isTableDef,
22457
22322
  isTreeConfig,
@@ -22549,6 +22414,8 @@ async function bulkUpsert(session, table, rows, options = {}) {
22549
22414
  repeat,
22550
22415
  replace,
22551
22416
  replaceWithRefs,
22417
+ requireProcedureCompiler,
22418
+ resolveDialectInput,
22552
22419
  resolveTreeConfig,
22553
22420
  resolveValidator,
22554
22421
  responseToRef,