metal-orm 1.0.89 → 1.0.91

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 (57) hide show
  1. package/dist/index.cjs +2968 -2983
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +784 -251
  4. package/dist/index.d.ts +784 -251
  5. package/dist/index.js +2913 -2975
  6. package/dist/index.js.map +1 -1
  7. package/package.json +6 -3
  8. package/src/codegen/typescript.ts +29 -40
  9. package/src/core/ast/expression-builders.ts +34 -53
  10. package/src/core/ast/expression-nodes.ts +51 -72
  11. package/src/core/ast/expression-visitor.ts +219 -252
  12. package/src/core/ast/expression.ts +20 -21
  13. package/src/core/ddl/introspect/utils.ts +45 -45
  14. package/src/core/dialect/abstract.ts +55 -81
  15. package/src/core/execution/db-executor.ts +4 -5
  16. package/src/core/execution/executors/mysql-executor.ts +7 -9
  17. package/src/decorators/bootstrap.ts +29 -26
  18. package/src/dto/apply-filter.ts +279 -0
  19. package/src/dto/dto-types.ts +229 -0
  20. package/src/dto/filter-types.ts +193 -0
  21. package/src/dto/index.ts +97 -0
  22. package/src/dto/openapi/generators/base.ts +29 -0
  23. package/src/dto/openapi/generators/column.ts +34 -0
  24. package/src/dto/openapi/generators/dto.ts +94 -0
  25. package/src/dto/openapi/generators/filter.ts +74 -0
  26. package/src/dto/openapi/generators/nested-dto.ts +532 -0
  27. package/src/dto/openapi/generators/pagination.ts +111 -0
  28. package/src/dto/openapi/generators/relation-filter.ts +210 -0
  29. package/src/dto/openapi/index.ts +17 -0
  30. package/src/dto/openapi/type-mappings.ts +191 -0
  31. package/src/dto/openapi/types.ts +90 -0
  32. package/src/dto/openapi/utilities.ts +45 -0
  33. package/src/dto/pagination-utils.ts +150 -0
  34. package/src/dto/transform.ts +197 -0
  35. package/src/index.ts +5 -3
  36. package/src/orm/entity-context.ts +9 -9
  37. package/src/orm/entity.ts +74 -74
  38. package/src/orm/orm-session.ts +159 -159
  39. package/src/orm/relation-change-processor.ts +3 -3
  40. package/src/orm/runtime-types.ts +5 -5
  41. package/src/orm/unit-of-work.ts +13 -25
  42. package/src/query-builder/query-ast-service.ts +287 -300
  43. package/src/query-builder/relation-filter-utils.ts +159 -160
  44. package/src/query-builder/select.ts +137 -192
  45. package/src/schema/column-types.ts +4 -4
  46. package/src/schema/types.ts +5 -1
  47. package/src/core/ast/ast-validation.ts +0 -19
  48. package/src/core/ast/param-proxy.ts +0 -47
  49. package/src/core/ast/query-visitor.ts +0 -273
  50. package/src/openapi/index.ts +0 -4
  51. package/src/openapi/query-parameters.ts +0 -207
  52. package/src/openapi/schema-extractor-input.ts +0 -193
  53. package/src/openapi/schema-extractor-output.ts +0 -427
  54. package/src/openapi/schema-extractor-utils.ts +0 -110
  55. package/src/openapi/schema-extractor.ts +0 -120
  56. package/src/openapi/schema-types.ts +0 -187
  57. package/src/openapi/type-mappers.ts +0 -227
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metal-orm",
3
- "version": "1.0.89",
3
+ "version": "1.0.91",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "engines": {
@@ -30,6 +30,7 @@
30
30
  "test": "vitest",
31
31
  "test:ui": "vitest --ui",
32
32
  "test:mysql": "vitest --run tests/e2e/mysql-memory.test.ts tests/e2e/decorators-mysql-memory.test.ts tests/e2e/save-graph-mysql-memory.test.ts",
33
+ "test:mysql:optimized": "vitest --run --config tests/e2e/mysql/vitest.config.ts",
33
34
  "test:sqlite": "vitest --run tests/e2e/sqlite-memory.test.ts tests/e2e/decorators-sqlite-memory.test.ts tests/e2e/save-graph-sqlite-memory.test.ts",
34
35
  "test:pglite": "vitest --run tests/e2e/pglite-memory.test.ts",
35
36
  "test:e2e": "vitest tests/e2e",
@@ -58,8 +59,6 @@
58
59
  }
59
60
  },
60
61
  "devDependencies": {
61
- "@electric-sql/pglite": "^0.3.14",
62
- "@types/express": "^5.0.6",
63
62
  "@typescript-eslint/eslint-plugin": "^8.20.0",
64
63
  "@typescript-eslint/parser": "^8.20.0",
65
64
  "@vitest/ui": "^4.0.14",
@@ -70,9 +69,13 @@
70
69
  "mysql2": "^3.15.3",
71
70
  "pg": "^8.16.3",
72
71
  "sqlite3": "^5.1.7",
72
+ "supertest": "^7.2.2",
73
73
  "tedious": "^19.1.3",
74
74
  "tsup": "^8.0.0",
75
75
  "typescript": "^5.5.0",
76
76
  "vitest": "^4.0.14"
77
+ },
78
+ "dependencies": {
79
+ "@electric-sql/pglite": "^0.3.14"
77
80
  }
78
81
  }
@@ -17,14 +17,13 @@ import {
17
17
  LiteralNode,
18
18
  FunctionNode,
19
19
  AliasRefNode,
20
- CastExpressionNode,
21
- CollateExpressionNode,
22
- ParamNode,
23
- ExpressionVisitor,
24
- OperandVisitor,
25
- visitExpression,
26
- visitOperand
27
- } from '../core/ast/expression.js';
20
+ CastExpressionNode,
21
+ CollateExpressionNode,
22
+ ExpressionVisitor,
23
+ OperandVisitor,
24
+ visitExpression,
25
+ visitOperand
26
+ } from '../core/ast/expression.js';
28
27
  import { SQL_OPERATOR_REGISTRY } from '../core/sql/sql-operator-config.js';
29
28
  import { SqlOperator } from '../core/sql/sql.js';
30
29
  import { isRelationAlias } from '../query-builder/relation-alias.js';
@@ -191,14 +190,13 @@ export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVi
191
190
  case 'ScalarSubquery':
192
191
  case 'CaseExpression':
193
192
  case 'WindowFunction':
194
- case 'Cast':
195
- case 'Collate':
196
- case 'Param':
197
- return this.printOperand(term);
198
- default:
199
- return this.printExpression(term);
200
- }
201
- }
193
+ case 'Cast':
194
+ case 'Collate':
195
+ return this.printOperand(term);
196
+ default:
197
+ return this.printExpression(term);
198
+ }
199
+ }
202
200
 
203
201
  private getSelectionKey(selection: SelectionColumn, index: number): string {
204
202
  if (selection.alias) {
@@ -246,17 +244,13 @@ export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVi
246
244
  return this.printColumnOperand(node);
247
245
  }
248
246
 
249
- public visitLiteral(node: LiteralNode): string {
250
- return this.printLiteralOperand(node);
251
- }
252
-
253
- public visitParam(node: ParamNode): string {
254
- return this.printParamOperand(node);
255
- }
256
-
257
- public visitFunction(node: FunctionNode): string {
258
- return this.printFunctionOperand(node);
259
- }
247
+ public visitLiteral(node: LiteralNode): string {
248
+ return this.printLiteralOperand(node);
249
+ }
250
+
251
+ public visitFunction(node: FunctionNode): string {
252
+ return this.printFunctionOperand(node);
253
+ }
260
254
 
261
255
  public visitJsonPath(node: JsonPathNode): string {
262
256
  return this.printJsonPathOperand(node);
@@ -385,19 +379,14 @@ export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVi
385
379
  * @param literal - Literal node
386
380
  * @returns TypeScript code representation
387
381
  */
388
- private printLiteralOperand(literal: LiteralNode): string {
389
- if (literal.value === null) return 'null';
390
- return typeof literal.value === 'string' ? `'${literal.value}'` : String(literal.value);
391
- }
392
-
393
- private printParamOperand(param: ParamNode): string {
394
- const name = param.name.replace(/'/g, "\\'");
395
- return `{ type: 'Param', name: '${name}' }`;
396
- }
397
-
398
- /**
399
- * Prints a function operand to TypeScript code
400
- * @param fn - Function node
382
+ private printLiteralOperand(literal: LiteralNode): string {
383
+ if (literal.value === null) return 'null';
384
+ return typeof literal.value === 'string' ? `'${literal.value}'` : String(literal.value);
385
+ }
386
+
387
+ /**
388
+ * Prints a function operand to TypeScript code
389
+ * @param fn - Function node
401
390
  * @returns TypeScript code representation
402
391
  */
403
392
  private printFunctionOperand(fn: FunctionNode): string {
@@ -2,14 +2,13 @@ import { SelectQueryNode } from './query.js';
2
2
  import { SqlOperator, BitwiseOperator } from '../sql/sql.js';
3
3
  import { ColumnRef } from './types.js';
4
4
  import {
5
- ColumnNode,
6
- LiteralNode,
7
- ParamNode,
8
- JsonPathNode,
9
- OperandNode,
10
- CaseExpressionNode,
11
- CastExpressionNode,
12
- BinaryExpressionNode,
5
+ ColumnNode,
6
+ LiteralNode,
7
+ JsonPathNode,
8
+ OperandNode,
9
+ CaseExpressionNode,
10
+ CastExpressionNode,
11
+ BinaryExpressionNode,
13
12
  ExpressionNode,
14
13
  LogicalExpressionNode,
15
14
  NullExpressionNode,
@@ -33,36 +32,27 @@ export type TypedLike<T> = { tsType?: T } | { __tsType: T };
33
32
  /**
34
33
  * Type guard to check if a value is a literal value
35
34
  */
36
- const isLiteralValue = (value: unknown): value is LiteralValue =>
37
- value === null ||
38
- typeof value === 'string' ||
39
- typeof value === 'number' ||
40
- typeof value === 'boolean' ||
41
- value instanceof Date;
35
+ const isLiteralValue = (value: unknown): value is LiteralValue =>
36
+ value === null ||
37
+ typeof value === 'string' ||
38
+ typeof value === 'number' ||
39
+ typeof value === 'boolean' ||
40
+ value instanceof Date;
42
41
 
43
42
 
44
43
  /**
45
44
  * Converts a primitive value to a LiteralNode
46
45
  */
47
- const toLiteralNode = (value: LiteralValue): LiteralNode => ({
48
- type: 'Literal',
49
- value: value instanceof Date ? value.toISOString() : value
50
- });
51
-
52
- const toParamNode = (value: unknown): ParamNode | undefined => {
53
- if (typeof value !== 'object' || value === null) return undefined;
54
- const type = Object.getOwnPropertyDescriptor(value, 'type')?.value;
55
- if (type !== 'Param') return undefined;
56
- const name = Object.getOwnPropertyDescriptor(value, 'name')?.value;
57
- if (typeof name !== 'string') return undefined;
58
- return { type: 'Param', name };
59
- };
60
-
61
- /**
62
- * Converts a ColumnRef to a ColumnNode
63
- * @throws Error if the ColumnRef doesn't have a table specified
64
- */
65
- const columnRefToNode = (col: ColumnRef): ColumnNode => {
46
+ const toLiteralNode = (value: LiteralValue): LiteralNode => ({
47
+ type: 'Literal',
48
+ value: value instanceof Date ? value.toISOString() : value
49
+ });
50
+
51
+ /**
52
+ * Converts a ColumnRef to a ColumnNode
53
+ * @throws Error if the ColumnRef doesn't have a table specified
54
+ */
55
+ const columnRefToNode = (col: ColumnRef): ColumnNode => {
66
56
  if (!col.table) {
67
57
  throw new Error(
68
58
  `Column "${col.name}" requires a table reference. ` +
@@ -77,16 +67,11 @@ const columnRefToNode = (col: ColumnRef): ColumnNode => {
77
67
  * @param value - Value to convert (OperandNode, ColumnRef, or literal value)
78
68
  * @returns OperandNode representing the value
79
69
  */
80
- const toOperandNode = (value: OperandNode | ColumnRef | LiteralValue): OperandNode => {
81
- const paramNode = toParamNode(value);
82
- if (paramNode) {
83
- return paramNode;
84
- }
85
-
86
- // Already an operand node
87
- if (isOperandNode(value)) {
88
- return value;
89
- }
70
+ const toOperandNode = (value: OperandNode | ColumnRef | LiteralValue): OperandNode => {
71
+ // Already an operand node
72
+ if (isOperandNode(value)) {
73
+ return value;
74
+ }
90
75
 
91
76
  // Literal value
92
77
  if (isLiteralValue(value)) {
@@ -102,16 +87,12 @@ const toOperandNode = (value: OperandNode | ColumnRef | LiteralValue): OperandNo
102
87
  * @param value - Value or operand to normalize
103
88
  * @returns OperandNode representing the value
104
89
  */
105
- export const valueToOperand = (value: ValueOperandInput): OperandNode => {
106
- const paramNode = toParamNode(value);
107
- if (paramNode) {
108
- return paramNode;
109
- }
110
- if (isOperandNode(value)) {
111
- return value;
112
- }
113
- return toLiteralNode(value);
114
- };
90
+ export const valueToOperand = (value: ValueOperandInput): OperandNode => {
91
+ if (isOperandNode(value)) {
92
+ return value;
93
+ }
94
+ return toLiteralNode(value);
95
+ };
115
96
 
116
97
  /**
117
98
  * Converts various input types to an OperandNode
@@ -2,23 +2,14 @@ import type { SelectQueryNode, OrderByNode } from './query.js';
2
2
  import { SqlOperator, BitwiseOperator } from '../sql/sql.js';
3
3
  import { ColumnRef } from './types.js';
4
4
 
5
- /**
6
- * AST node representing a literal value
7
- */
8
- export interface LiteralNode {
9
- type: 'Literal';
10
- /** The literal value (string, number, boolean, Date, or null) */
11
- value: string | number | boolean | Date | null;
12
- }
13
-
14
- /**
15
- * AST node representing a named parameter placeholder
16
- */
17
- export interface ParamNode {
18
- type: 'Param';
19
- /** Stable parameter name */
20
- name: string;
21
- }
5
+ /**
6
+ * AST node representing a literal value
7
+ */
8
+ export interface LiteralNode {
9
+ type: 'Literal';
10
+ /** The literal value (string, number, boolean, Date, or null) */
11
+ value: string | number | boolean | Date | null;
12
+ }
22
13
 
23
14
  /**
24
15
  * AST node representing a reference to a SELECT alias (for ORDER BY / GROUP BY).
@@ -158,66 +149,54 @@ export interface ArithmeticExpressionNode {
158
149
  /**
159
150
  * Union type representing any operand that can be used in expressions
160
151
  */
161
- export type OperandNode =
162
- | AliasRefNode
163
- | ColumnNode
164
- | LiteralNode
165
- | ParamNode
166
- | FunctionNode
167
- | JsonPathNode
168
- | ScalarSubqueryNode
169
- | CaseExpressionNode
170
- | CastExpressionNode
152
+ export type OperandNode =
153
+ | AliasRefNode
154
+ | ColumnNode
155
+ | LiteralNode
156
+ | FunctionNode
157
+ | JsonPathNode
158
+ | ScalarSubqueryNode
159
+ | CaseExpressionNode
160
+ | CastExpressionNode
171
161
  | WindowFunctionNode
172
162
  | ArithmeticExpressionNode
173
163
  | BitwiseExpressionNode
174
164
  | CollateExpressionNode;
175
165
 
176
- const operandTypes = new Set<OperandNode['type']>([
177
- 'AliasRef',
178
- 'Column',
179
- 'Literal',
180
- 'Param',
181
- 'Function',
182
- 'JsonPath',
183
- 'ScalarSubquery',
184
- 'CaseExpression',
185
- 'Cast',
186
- 'WindowFunction',
187
- 'ArithmeticExpression',
188
- 'BitwiseExpression',
189
- 'Collate'
190
- ]);
191
-
192
- const getNodeType = (value: unknown): string | undefined => {
193
- if (typeof value !== 'object' || value === null) return undefined;
194
- const descriptor = Object.getOwnPropertyDescriptor(value, 'type');
195
- if (descriptor && typeof descriptor.value === 'string') {
196
- return descriptor.value;
197
- }
198
- if ('type' in value) {
199
- const type = (value as { type?: unknown }).type;
200
- return typeof type === 'string' ? type : undefined;
201
- }
202
- return undefined;
203
- };
204
-
205
- export const isOperandNode = (node: unknown): node is OperandNode => {
206
- const type = getNodeType(node);
207
- return type !== undefined && operandTypes.has(type as OperandNode['type']);
208
- };
209
-
210
- export const isFunctionNode = (node: unknown): node is FunctionNode =>
211
- isOperandNode(node) && getNodeType(node) === 'Function';
212
- export const isCaseExpressionNode = (node: unknown): node is CaseExpressionNode =>
213
- isOperandNode(node) && getNodeType(node) === 'CaseExpression';
214
-
215
- export const isCastExpressionNode = (node: unknown): node is CastExpressionNode =>
216
- isOperandNode(node) && getNodeType(node) === 'Cast';
217
- export const isCollateExpressionNode = (node: unknown): node is CollateExpressionNode =>
218
- isOperandNode(node) && getNodeType(node) === 'Collate';
219
- export const isWindowFunctionNode = (node: unknown): node is WindowFunctionNode =>
220
- isOperandNode(node) && getNodeType(node) === 'WindowFunction';
166
+ const operandTypes = new Set<OperandNode['type']>([
167
+ 'AliasRef',
168
+ 'Column',
169
+ 'Literal',
170
+ 'Function',
171
+ 'JsonPath',
172
+ 'ScalarSubquery',
173
+ 'CaseExpression',
174
+ 'Cast',
175
+ 'WindowFunction',
176
+ 'ArithmeticExpression',
177
+ 'BitwiseExpression',
178
+ 'Collate'
179
+ ]);
180
+
181
+ const hasTypeProperty = (value: unknown): value is { type?: string } =>
182
+ typeof value === 'object' && value !== null && 'type' in value;
183
+
184
+ export const isOperandNode = (node: unknown): node is OperandNode => {
185
+ if (!hasTypeProperty(node)) return false;
186
+ return operandTypes.has(node.type as OperandNode['type']);
187
+ };
188
+
189
+ export const isFunctionNode = (node: unknown): node is FunctionNode =>
190
+ isOperandNode(node) && node.type === 'Function';
191
+ export const isCaseExpressionNode = (node: unknown): node is CaseExpressionNode =>
192
+ isOperandNode(node) && node.type === 'CaseExpression';
193
+
194
+ export const isCastExpressionNode = (node: unknown): node is CastExpressionNode =>
195
+ isOperandNode(node) && node.type === 'Cast';
196
+ export const isCollateExpressionNode = (node: unknown): node is CollateExpressionNode =>
197
+ isOperandNode(node) && node.type === 'Collate';
198
+ export const isWindowFunctionNode = (node: unknown): node is WindowFunctionNode =>
199
+ isOperandNode(node) && node.type === 'WindowFunction';
221
200
  export const isExpressionSelectionNode = (
222
201
  node: ColumnRef | FunctionNode | CaseExpressionNode | CastExpressionNode | WindowFunctionNode
223
202
  ): node is FunctionNode | CaseExpressionNode | CastExpressionNode | WindowFunctionNode =>