metal-orm 1.0.89 → 1.0.90

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 (49) hide show
  1. package/dist/index.cjs +2968 -2983
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +765 -246
  4. package/dist/index.d.ts +765 -246
  5. package/dist/index.js +2913 -2975
  6. package/dist/index.js.map +1 -1
  7. package/package.json +3 -2
  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/dialect/abstract.ts +55 -81
  14. package/src/core/execution/db-executor.ts +4 -5
  15. package/src/core/execution/executors/mysql-executor.ts +7 -9
  16. package/src/decorators/bootstrap.ts +11 -8
  17. package/src/dto/apply-filter.ts +281 -0
  18. package/src/dto/dto-types.ts +229 -0
  19. package/src/dto/filter-types.ts +193 -0
  20. package/src/dto/index.ts +97 -0
  21. package/src/dto/openapi/generators/base.ts +29 -0
  22. package/src/dto/openapi/generators/column.ts +34 -0
  23. package/src/dto/openapi/generators/dto.ts +94 -0
  24. package/src/dto/openapi/generators/filter.ts +74 -0
  25. package/src/dto/openapi/generators/nested-dto.ts +532 -0
  26. package/src/dto/openapi/generators/pagination.ts +111 -0
  27. package/src/dto/openapi/generators/relation-filter.ts +210 -0
  28. package/src/dto/openapi/index.ts +17 -0
  29. package/src/dto/openapi/type-mappings.ts +191 -0
  30. package/src/dto/openapi/types.ts +83 -0
  31. package/src/dto/openapi/utilities.ts +45 -0
  32. package/src/dto/pagination-utils.ts +150 -0
  33. package/src/dto/transform.ts +193 -0
  34. package/src/index.ts +67 -65
  35. package/src/orm/unit-of-work.ts +13 -25
  36. package/src/query-builder/query-ast-service.ts +287 -300
  37. package/src/query-builder/relation-filter-utils.ts +159 -160
  38. package/src/query-builder/select.ts +137 -192
  39. package/src/core/ast/ast-validation.ts +0 -19
  40. package/src/core/ast/param-proxy.ts +0 -47
  41. package/src/core/ast/query-visitor.ts +0 -273
  42. package/src/openapi/index.ts +0 -4
  43. package/src/openapi/query-parameters.ts +0 -207
  44. package/src/openapi/schema-extractor-input.ts +0 -193
  45. package/src/openapi/schema-extractor-output.ts +0 -427
  46. package/src/openapi/schema-extractor-utils.ts +0 -110
  47. package/src/openapi/schema-extractor.ts +0 -120
  48. package/src/openapi/schema-types.ts +0 -187
  49. package/src/openapi/type-mappers.ts +0 -227
@@ -1,273 +0,0 @@
1
- import type {
2
- CommonTableExpressionNode,
3
- DerivedTableNode,
4
- FunctionTableNode,
5
- OrderByNode,
6
- OrderingTerm,
7
- SelectQueryNode,
8
- SetOperationNode,
9
- TableSourceNode
10
- } from './query.js';
11
- import type { JoinNode } from './join.js';
12
- import type {
13
- ExpressionNode,
14
- OperandNode,
15
- ParamNode
16
- } from './expression-nodes.js';
17
- import { isOperandNode } from './expression-nodes.js';
18
- import {
19
- hasOperandDispatcher,
20
- type ExpressionVisitor,
21
- type OperandVisitor,
22
- visitExpression,
23
- visitOperand
24
- } from './expression-visitor.js';
25
-
26
- export interface SelectQueryVisitor {
27
- visitSelectQuery?(node: SelectQueryNode): void;
28
- visitTableSource?(node: TableSourceNode): void;
29
- visitDerivedTable?(node: DerivedTableNode): void;
30
- visitFunctionTable?(node: FunctionTableNode): void;
31
- visitJoin?(node: JoinNode): void;
32
- visitCte?(node: CommonTableExpressionNode): void;
33
- visitSetOperation?(node: SetOperationNode): void;
34
- visitOrderBy?(node: OrderByNode): void;
35
- visitExpression?(node: ExpressionNode): void;
36
- visitOperand?(node: OperandNode): void;
37
- visitParam?(node: ParamNode): void;
38
- }
39
-
40
- const getNodeType = (value: unknown): string | undefined => {
41
- if (typeof value !== 'object' || value === null) return undefined;
42
- const descriptor = Object.getOwnPropertyDescriptor(value, 'type');
43
- if (descriptor && typeof descriptor.value === 'string') {
44
- return descriptor.value;
45
- }
46
- if ('type' in value) {
47
- const type = (value as { type?: unknown }).type;
48
- return typeof type === 'string' ? type : undefined;
49
- }
50
- return undefined;
51
- };
52
-
53
- export const visitSelectQuery = (ast: SelectQueryNode, visitor: SelectQueryVisitor): void => {
54
- const visitExpressionNode = (node: ExpressionNode): void => {
55
- visitExpression(node, expressionVisitor);
56
- };
57
-
58
- const visitOperandNode = (node: OperandNode): void => {
59
- visitOperand(node, operandVisitor);
60
- };
61
-
62
- const visitOrderingTerm = (term: OrderingTerm): void => {
63
- if (!term || typeof term !== 'object') return;
64
- if (isOperandNode(term)) {
65
- visitOperandNode(term);
66
- return;
67
- }
68
- const type = getNodeType(term);
69
- if (type && hasOperandDispatcher(type)) {
70
- visitOperandNode(term as unknown as OperandNode);
71
- return;
72
- }
73
- if (type) {
74
- visitExpressionNode(term as ExpressionNode);
75
- }
76
- };
77
-
78
- const visitOrderByNode = (node: OrderByNode): void => {
79
- visitor.visitOrderBy?.(node);
80
- visitOrderingTerm(node.term);
81
- };
82
-
83
- const visitTableSource = (source: TableSourceNode): void => {
84
- visitor.visitTableSource?.(source);
85
- if (source.type === 'DerivedTable') {
86
- visitor.visitDerivedTable?.(source);
87
- visitSelectQuery(source.query, visitor);
88
- return;
89
- }
90
- if (source.type === 'FunctionTable') {
91
- visitor.visitFunctionTable?.(source);
92
- source.args?.forEach(arg => visitOperandNode(arg));
93
- }
94
- };
95
-
96
- const expressionVisitor: ExpressionVisitor<void> = {
97
- visitBinaryExpression: (node) => {
98
- visitor.visitExpression?.(node);
99
- visitOperandNode(node.left);
100
- visitOperandNode(node.right);
101
- if (node.escape) {
102
- visitOperandNode(node.escape);
103
- }
104
- },
105
- visitLogicalExpression: (node) => {
106
- visitor.visitExpression?.(node);
107
- node.operands.forEach(operand => visitExpressionNode(operand));
108
- },
109
- visitNullExpression: (node) => {
110
- visitor.visitExpression?.(node);
111
- visitOperandNode(node.left);
112
- },
113
- visitInExpression: (node) => {
114
- visitor.visitExpression?.(node);
115
- visitOperandNode(node.left);
116
- if (Array.isArray(node.right)) {
117
- node.right.forEach(operand => visitOperandNode(operand));
118
- } else {
119
- visitOperandNode(node.right);
120
- }
121
- },
122
- visitExistsExpression: (node) => {
123
- visitor.visitExpression?.(node);
124
- visitSelectQuery(node.subquery, visitor);
125
- },
126
- visitBetweenExpression: (node) => {
127
- visitor.visitExpression?.(node);
128
- visitOperandNode(node.left);
129
- visitOperandNode(node.lower);
130
- visitOperandNode(node.upper);
131
- },
132
- visitArithmeticExpression: (node) => {
133
- visitor.visitExpression?.(node);
134
- visitOperandNode(node.left);
135
- visitOperandNode(node.right);
136
- },
137
- visitBitwiseExpression: (node) => {
138
- visitor.visitExpression?.(node);
139
- visitOperandNode(node.left);
140
- visitOperandNode(node.right);
141
- },
142
- visitOperand: (node) => {
143
- visitOperandNode(node);
144
- },
145
- visitSelectQuery: (node) => {
146
- visitSelectQuery(node, visitor);
147
- },
148
- otherwise: (node) => {
149
- visitor.visitExpression?.(node);
150
- }
151
- };
152
-
153
- const operandVisitor: OperandVisitor<void> = {
154
- visitColumn: (node) => {
155
- visitor.visitOperand?.(node);
156
- },
157
- visitLiteral: (node) => {
158
- visitor.visitOperand?.(node);
159
- },
160
- visitParam: (node) => {
161
- visitor.visitOperand?.(node);
162
- visitor.visitParam?.(node);
163
- },
164
- visitFunction: (node) => {
165
- visitor.visitOperand?.(node);
166
- node.args?.forEach(arg => visitOperandNode(arg));
167
- node.orderBy?.forEach(order => visitOrderByNode(order));
168
- if (node.separator) {
169
- visitOperandNode(node.separator);
170
- }
171
- },
172
- visitJsonPath: (node) => {
173
- visitor.visitOperand?.(node);
174
- visitOperandNode(node.column);
175
- },
176
- visitScalarSubquery: (node) => {
177
- visitor.visitOperand?.(node);
178
- visitSelectQuery(node.query, visitor);
179
- },
180
- visitCaseExpression: (node) => {
181
- visitor.visitOperand?.(node);
182
- node.conditions.forEach(cond => {
183
- visitExpressionNode(cond.when);
184
- visitOperandNode(cond.then);
185
- });
186
- if (node.else) {
187
- visitOperandNode(node.else);
188
- }
189
- },
190
- visitCast: (node) => {
191
- visitor.visitOperand?.(node);
192
- visitOperandNode(node.expression);
193
- },
194
- visitWindowFunction: (node) => {
195
- visitor.visitOperand?.(node);
196
- node.args?.forEach(arg => visitOperandNode(arg));
197
- node.partitionBy?.forEach(term => visitOperandNode(term));
198
- node.orderBy?.forEach(order => visitOrderByNode(order));
199
- },
200
- visitArithmeticExpression: (node) => {
201
- visitor.visitOperand?.(node);
202
- visitOperandNode(node.left);
203
- visitOperandNode(node.right);
204
- },
205
- visitBitwiseExpression: (node) => {
206
- visitor.visitOperand?.(node);
207
- visitOperandNode(node.left);
208
- visitOperandNode(node.right);
209
- },
210
- visitExpression: (node) => {
211
- visitExpressionNode(node);
212
- },
213
- visitSelectQuery: (node) => {
214
- visitSelectQuery(node, visitor);
215
- },
216
- visitCollate: (node) => {
217
- visitor.visitOperand?.(node);
218
- visitOperandNode(node.expression);
219
- },
220
- visitAliasRef: (node) => {
221
- visitor.visitOperand?.(node);
222
- },
223
- otherwise: (node) => {
224
- visitor.visitOperand?.(node);
225
- }
226
- };
227
-
228
- visitor.visitSelectQuery?.(ast);
229
-
230
- if (ast.ctes) {
231
- for (const cte of ast.ctes) {
232
- visitor.visitCte?.(cte);
233
- visitSelectQuery(cte.query, visitor);
234
- }
235
- }
236
-
237
- visitTableSource(ast.from);
238
-
239
- ast.columns?.forEach(col => {
240
- visitOperandNode(col as OperandNode);
241
- });
242
-
243
- ast.joins?.forEach(join => {
244
- visitor.visitJoin?.(join);
245
- visitTableSource(join.table);
246
- visitExpressionNode(join.condition);
247
- });
248
-
249
- if (ast.where) {
250
- visitExpressionNode(ast.where);
251
- }
252
-
253
- ast.groupBy?.forEach(term => {
254
- visitOrderingTerm(term);
255
- });
256
-
257
- if (ast.having) {
258
- visitExpressionNode(ast.having);
259
- }
260
-
261
- ast.orderBy?.forEach(order => {
262
- visitOrderByNode(order);
263
- });
264
-
265
- ast.distinct?.forEach(col => {
266
- visitOperandNode(col);
267
- });
268
-
269
- ast.setOps?.forEach(op => {
270
- visitor.visitSetOperation?.(op);
271
- visitSelectQuery(op.query, visitor);
272
- });
273
- };
@@ -1,4 +0,0 @@
1
- export * from './schema-types.js';
2
- export * from './type-mappers.js';
3
- export * from './schema-extractor.js';
4
- export * from './query-parameters.js';
@@ -1,207 +0,0 @@
1
- import type { TableDef } from '../schema/table.js';
2
- import {
3
- isOperandNode,
4
- type ExpressionNode,
5
- type OperandNode,
6
- type ColumnNode,
7
- type FunctionNode,
8
- type JsonPathNode,
9
- type CaseExpressionNode,
10
- type CastExpressionNode,
11
- type WindowFunctionNode,
12
- type ArithmeticExpressionNode,
13
- type BitwiseExpressionNode,
14
- type CollateExpressionNode
15
- } from '../core/ast/expression.js';
16
- import type { TableSourceNode, OrderByNode, OrderingTerm } from '../core/ast/query.js';
17
- import type { ColumnSchemaOptions, JsonSchemaProperty, OpenApiParameter } from './schema-types.js';
18
- import { mapColumnType } from './type-mappers.js';
19
-
20
- const FILTER_PARAM_NAME = 'filter';
21
-
22
- const buildRootTableNames = (table: TableDef, from?: TableSourceNode): Set<string> => {
23
- const names = new Set<string>([table.name]);
24
- if (from?.type === 'Table') {
25
- names.add(from.name);
26
- if (from.alias) names.add(from.alias);
27
- }
28
- return names;
29
- };
30
-
31
- const collectFilterColumns = (
32
- expr: ExpressionNode,
33
- table: TableDef,
34
- rootTables: Set<string>
35
- ): Set<string> => {
36
- const columns = new Set<string>();
37
-
38
- const recordColumn = (node: ColumnNode): void => {
39
- if (!rootTables.has(node.table)) return;
40
- if (node.name in table.columns) {
41
- columns.add(node.name);
42
- }
43
- };
44
-
45
- const visitOrderingTerm = (term: OrderingTerm): void => {
46
- if (!term || typeof term !== 'object') return;
47
- if (isOperandNode(term)) {
48
- visitOperand(term as OperandNode);
49
- return;
50
- }
51
- if ('type' in term) {
52
- visitExpression(term as ExpressionNode);
53
- }
54
- };
55
-
56
- const visitOrderBy = (orderBy: OrderByNode[] | undefined): void => {
57
- if (!orderBy) return;
58
- orderBy.forEach(node => visitOrderingTerm(node.term));
59
- };
60
-
61
- const visitOperand = (node: OperandNode): void => {
62
- switch (node.type) {
63
- case 'Column':
64
- recordColumn(node as ColumnNode);
65
- return;
66
- case 'Function': {
67
- const fn = node as FunctionNode;
68
- fn.args?.forEach(visitOperand);
69
- visitOrderBy(fn.orderBy);
70
- if (fn.separator) visitOperand(fn.separator);
71
- return;
72
- }
73
- case 'JsonPath': {
74
- const jp = node as JsonPathNode;
75
- recordColumn(jp.column);
76
- return;
77
- }
78
- case 'ScalarSubquery':
79
- return;
80
- case 'CaseExpression': {
81
- const cs = node as CaseExpressionNode;
82
- cs.conditions.forEach(condition => {
83
- visitExpression(condition.when);
84
- visitOperand(condition.then);
85
- });
86
- if (cs.else) visitOperand(cs.else);
87
- return;
88
- }
89
- case 'Cast': {
90
- const cast = node as CastExpressionNode;
91
- visitOperand(cast.expression);
92
- return;
93
- }
94
- case 'WindowFunction': {
95
- const windowFn = node as WindowFunctionNode;
96
- windowFn.args?.forEach(visitOperand);
97
- windowFn.partitionBy?.forEach(recordColumn);
98
- visitOrderBy(windowFn.orderBy);
99
- return;
100
- }
101
- case 'ArithmeticExpression': {
102
- const arith = node as ArithmeticExpressionNode;
103
- visitOperand(arith.left);
104
- visitOperand(arith.right);
105
- return;
106
- }
107
- case 'BitwiseExpression': {
108
- const bitwise = node as BitwiseExpressionNode;
109
- visitOperand(bitwise.left);
110
- visitOperand(bitwise.right);
111
- return;
112
- }
113
- case 'Collate': {
114
- const collate = node as CollateExpressionNode;
115
- visitOperand(collate.expression);
116
- return;
117
- }
118
- case 'AliasRef':
119
- case 'Literal':
120
- case 'Param':
121
- return;
122
- default:
123
- return;
124
- }
125
- };
126
-
127
- const visitExpression = (node: ExpressionNode): void => {
128
- switch (node.type) {
129
- case 'BinaryExpression':
130
- visitOperand(node.left);
131
- visitOperand(node.right);
132
- if (node.escape) visitOperand(node.escape);
133
- return;
134
- case 'LogicalExpression':
135
- node.operands.forEach(visitExpression);
136
- return;
137
- case 'NullExpression':
138
- visitOperand(node.left);
139
- return;
140
- case 'InExpression':
141
- visitOperand(node.left);
142
- if (Array.isArray(node.right)) {
143
- node.right.forEach(visitOperand);
144
- }
145
- return;
146
- case 'ExistsExpression':
147
- return;
148
- case 'BetweenExpression':
149
- visitOperand(node.left);
150
- visitOperand(node.lower);
151
- visitOperand(node.upper);
152
- return;
153
- case 'ArithmeticExpression':
154
- visitOperand(node.left);
155
- visitOperand(node.right);
156
- return;
157
- case 'BitwiseExpression':
158
- visitOperand(node.left);
159
- visitOperand(node.right);
160
- return;
161
- default:
162
- return;
163
- }
164
- };
165
-
166
- visitExpression(expr);
167
- return columns;
168
- };
169
-
170
- export const buildFilterParameters = (
171
- table: TableDef,
172
- where: ExpressionNode | undefined,
173
- from: TableSourceNode | undefined,
174
- options: ColumnSchemaOptions = {}
175
- ): OpenApiParameter[] => {
176
- if (!where) return [];
177
-
178
- const rootTables = buildRootTableNames(table, from);
179
- const columnNames = collectFilterColumns(where, table, rootTables);
180
-
181
- let schema: JsonSchemaProperty;
182
- if (columnNames.size) {
183
- const properties: Record<string, JsonSchemaProperty> = {};
184
- for (const name of columnNames) {
185
- const column = table.columns[name];
186
- if (!column) continue;
187
- properties[name] = mapColumnType(column, options);
188
- }
189
- schema = {
190
- type: 'object',
191
- properties
192
- };
193
- } else {
194
- schema = {
195
- type: 'object',
196
- additionalProperties: true
197
- };
198
- }
199
-
200
- return [{
201
- name: FILTER_PARAM_NAME,
202
- in: 'query',
203
- style: 'deepObject',
204
- explode: true,
205
- schema
206
- }];
207
- };
@@ -1,193 +0,0 @@
1
- import type { TableDef } from '../schema/table.js';
2
- import type { RelationDef } from '../schema/relation.js';
3
- import { RelationKinds } from '../schema/relation.js';
4
- import { findPrimaryKey } from '../query-builder/hydration-planner.js';
5
-
6
- import type {
7
- OpenApiSchema,
8
- SchemaExtractionContext,
9
- InputSchemaOptions,
10
- RelationSelection,
11
- JsonSchemaProperty,
12
- JsonSchemaType
13
- } from './schema-types.js';
14
- import { mapColumnType, mapRelationType } from './type-mappers.js';
15
- import { buildCircularReferenceSchema } from './schema-extractor-utils.js';
16
-
17
- /**
18
- * Input schema extraction (write payloads)
19
- */
20
- export const extractInputSchema = (
21
- table: TableDef,
22
- context: SchemaExtractionContext,
23
- options: InputSchemaOptions
24
- ): OpenApiSchema => {
25
- const cacheKey = `${table.name}:${options.mode ?? 'create'}`;
26
-
27
- if (context.schemaCache.has(cacheKey)) {
28
- return context.schemaCache.get(cacheKey)!;
29
- }
30
-
31
- if (context.visitedTables.has(cacheKey) && context.depth > 0) {
32
- return buildCircularReferenceSchema(table.name, 'input');
33
- }
34
-
35
- context.visitedTables.add(cacheKey);
36
-
37
- const properties: Record<string, JsonSchemaProperty> = {};
38
- const required: string[] = [];
39
- const primaryKey = findPrimaryKey(table);
40
-
41
- for (const [columnName, column] of Object.entries(table.columns)) {
42
- const isPrimary = columnName === primaryKey || column.primary;
43
- if (options.excludePrimaryKey && isPrimary) continue;
44
- if (options.omitReadOnly && isReadOnlyColumn(column)) continue;
45
-
46
- properties[columnName] = mapColumnType(column, options);
47
-
48
- if (options.mode === 'create' && isRequiredForCreate(column)) {
49
- required.push(columnName);
50
- }
51
-
52
- if (options.mode === 'update' && options.requirePrimaryKey && isPrimary) {
53
- required.push(columnName);
54
- }
55
- }
56
-
57
- if (options.includeRelations && context.depth < context.maxDepth) {
58
- for (const [relationName, relation] of Object.entries(table.relations)) {
59
- properties[relationName] = extractInputRelationSchema(
60
- relationName,
61
- relation,
62
- { ...context, depth: context.depth + 1 },
63
- options
64
- );
65
- }
66
- }
67
-
68
- const schema: OpenApiSchema = {
69
- type: 'object',
70
- properties,
71
- required
72
- };
73
-
74
- context.schemaCache.set(cacheKey, schema);
75
- return schema;
76
- };
77
-
78
- const isReadOnlyColumn = (column: { autoIncrement?: boolean; generated?: string }): boolean =>
79
- Boolean(column.autoIncrement || column.generated === 'always');
80
-
81
- const isRequiredForCreate = (column: { notNull?: boolean; primary?: boolean; default?: unknown; autoIncrement?: boolean; generated?: string }): boolean => {
82
- if (isReadOnlyColumn(column)) return false;
83
- if (column.default !== undefined) return false;
84
- return Boolean(column.notNull || column.primary);
85
- };
86
-
87
- const buildPrimaryKeySchema = (
88
- table: TableDef,
89
- options: InputSchemaOptions
90
- ): JsonSchemaProperty => {
91
- const primaryKey = findPrimaryKey(table);
92
- const column = table.columns[primaryKey];
93
- if (!column) {
94
- return {
95
- anyOf: [
96
- { type: 'string' as JsonSchemaType },
97
- { type: 'number' as JsonSchemaType },
98
- { type: 'integer' as JsonSchemaType }
99
- ]
100
- };
101
- }
102
-
103
- return mapColumnType(column, options);
104
- };
105
-
106
- const extractInputRelationSchema = (
107
- relationName: string,
108
- relation: RelationDef,
109
- context: SchemaExtractionContext,
110
- options: InputSchemaOptions
111
- ): JsonSchemaProperty => {
112
- const { type: relationType, isNullable } = mapRelationType(relation.type);
113
- const relationMode = options.relationMode ?? 'mixed';
114
- const allowIds = relationMode !== 'objects';
115
- const allowObjects = relationMode !== 'ids';
116
-
117
- const variants: JsonSchemaProperty[] = [];
118
-
119
- if (allowIds) {
120
- variants.push(buildPrimaryKeySchema(relation.target, options));
121
- }
122
-
123
- if (allowObjects) {
124
- let targetSchema = extractInputSchema(relation.target, context, options);
125
- targetSchema = applyRelationSelection(targetSchema, options.relationSelections?.[relationName]);
126
- if (options.excludeRelationForeignKeys && isRelationForeignKeyToParent(relation)) {
127
- targetSchema = removeForeignKey(targetSchema, relation.foreignKey);
128
- }
129
- variants.push(targetSchema as JsonSchemaProperty);
130
- }
131
-
132
- const itemSchema: JsonSchemaProperty =
133
- variants.length === 1 ? variants[0] : { anyOf: variants };
134
-
135
- if (relationType === 'array') {
136
- return {
137
- type: 'array',
138
- items: itemSchema,
139
- nullable: isNullable
140
- };
141
- }
142
-
143
- return {
144
- ...itemSchema,
145
- nullable: isNullable
146
- };
147
- };
148
-
149
- const applyRelationSelection = (
150
- schema: OpenApiSchema,
151
- selection?: RelationSelection
152
- ): OpenApiSchema => {
153
- if (!selection || (selection.pick === undefined && selection.omit === undefined)) {
154
- return schema;
155
- }
156
-
157
- const hasPick = selection.pick !== undefined;
158
- const pick = hasPick ? new Set(selection.pick ?? []) : undefined;
159
- const omit = selection.omit !== undefined ? new Set(selection.omit ?? []) : undefined;
160
- const properties = Object.entries(schema.properties).reduce<Record<string, JsonSchemaProperty>>(
161
- (acc, [key, value]) => {
162
- if (pick && !pick.has(key)) return acc;
163
- if (omit && omit.has(key)) return acc;
164
- acc[key] = value;
165
- return acc;
166
- },
167
- {}
168
- );
169
- const required = schema.required.filter(name => properties[name] !== undefined);
170
-
171
- return {
172
- ...schema,
173
- properties,
174
- required
175
- };
176
- };
177
-
178
- const removeForeignKey = (schema: OpenApiSchema, foreignKey?: string): OpenApiSchema => {
179
- if (!foreignKey || !schema.properties[foreignKey]) return schema;
180
- const properties = { ...schema.properties };
181
- delete properties[foreignKey];
182
- const required = schema.required.filter(name => name !== foreignKey);
183
-
184
- return {
185
- ...schema,
186
- properties,
187
- required
188
- };
189
- };
190
-
191
- const isRelationForeignKeyToParent = (relation: RelationDef): relation is RelationDef & { foreignKey: string } => {
192
- return relation.type === RelationKinds.HasMany || relation.type === RelationKinds.HasOne;
193
- };