metal-orm 1.0.80 → 1.0.82
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 +460 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +99 -24
- package/dist/index.d.ts +99 -24
- package/dist/index.js +458 -71
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/src/codegen/typescript.ts +40 -29
- package/src/core/ast/expression-nodes.ts +32 -21
- package/src/core/ast/expression-visitor.ts +6 -1
- package/src/core/ast/expression.ts +1 -0
- package/src/core/ast/param-proxy.ts +50 -0
- package/src/core/dialect/abstract.ts +12 -10
- package/src/core/execution/db-executor.ts +5 -4
- package/src/core/execution/executors/mysql-executor.ts +9 -7
- package/src/openapi/index.ts +1 -0
- package/src/openapi/query-parameters.ts +207 -0
- package/src/openapi/schema-extractor.ts +290 -122
- package/src/openapi/schema-types.ts +72 -6
- package/src/openapi/type-mappers.ts +28 -8
- package/src/orm/unit-of-work.ts +25 -13
- package/src/query-builder/relation-filter-utils.ts +1 -0
- package/src/query-builder/select.ts +17 -7
package/dist/index.cjs
CHANGED
|
@@ -96,6 +96,7 @@ __export(index_exports, {
|
|
|
96
96
|
bitOr: () => bitOr,
|
|
97
97
|
bitXor: () => bitXor,
|
|
98
98
|
bootstrapEntities: () => bootstrapEntities,
|
|
99
|
+
buildFilterParameters: () => buildFilterParameters,
|
|
99
100
|
caseWhen: () => caseWhen,
|
|
100
101
|
cast: () => cast,
|
|
101
102
|
cbrt: () => cbrt,
|
|
@@ -122,6 +123,7 @@ __export(index_exports, {
|
|
|
122
123
|
createExecutorFromQueryRunner: () => createExecutorFromQueryRunner,
|
|
123
124
|
createMssqlExecutor: () => createMssqlExecutor,
|
|
124
125
|
createMysqlExecutor: () => createMysqlExecutor,
|
|
126
|
+
createParamProxy: () => createParamProxy,
|
|
125
127
|
createPooledExecutorFactory: () => createPooledExecutorFactory,
|
|
126
128
|
createPostgresExecutor: () => createPostgresExecutor,
|
|
127
129
|
createQueryLoggingExecutor: () => createQueryLoggingExecutor,
|
|
@@ -642,6 +644,7 @@ var operandTypes = /* @__PURE__ */ new Set([
|
|
|
642
644
|
"AliasRef",
|
|
643
645
|
"Column",
|
|
644
646
|
"Literal",
|
|
647
|
+
"Param",
|
|
645
648
|
"Function",
|
|
646
649
|
"JsonPath",
|
|
647
650
|
"ScalarSubquery",
|
|
@@ -1081,6 +1084,9 @@ var visitOperand = (node, visitor) => {
|
|
|
1081
1084
|
case "Literal":
|
|
1082
1085
|
if (visitor.visitLiteral) return visitor.visitLiteral(node);
|
|
1083
1086
|
break;
|
|
1087
|
+
case "Param":
|
|
1088
|
+
if (visitor.visitParam) return visitor.visitParam(node);
|
|
1089
|
+
break;
|
|
1084
1090
|
case "Function":
|
|
1085
1091
|
if (visitor.visitFunction) return visitor.visitFunction(node);
|
|
1086
1092
|
break;
|
|
@@ -1112,6 +1118,47 @@ var visitOperand = (node, visitor) => {
|
|
|
1112
1118
|
return unsupportedOperand(node);
|
|
1113
1119
|
};
|
|
1114
1120
|
|
|
1121
|
+
// src/core/ast/param-proxy.ts
|
|
1122
|
+
var buildParamProxy = (name) => {
|
|
1123
|
+
const target = { type: "Param", name };
|
|
1124
|
+
return new Proxy(target, {
|
|
1125
|
+
get(t, prop, receiver) {
|
|
1126
|
+
if (prop === "then") return void 0;
|
|
1127
|
+
if (typeof prop === "symbol") {
|
|
1128
|
+
return Reflect.get(t, prop, receiver);
|
|
1129
|
+
}
|
|
1130
|
+
if (typeof prop === "string" && prop.startsWith("$")) {
|
|
1131
|
+
const trimmed = prop.slice(1);
|
|
1132
|
+
const nextName2 = name ? `${name}.${trimmed}` : trimmed;
|
|
1133
|
+
return buildParamProxy(nextName2);
|
|
1134
|
+
}
|
|
1135
|
+
if (prop in t) {
|
|
1136
|
+
return t[prop];
|
|
1137
|
+
}
|
|
1138
|
+
const nextName = name ? `${name}.${prop}` : prop;
|
|
1139
|
+
return buildParamProxy(nextName);
|
|
1140
|
+
}
|
|
1141
|
+
});
|
|
1142
|
+
};
|
|
1143
|
+
var createParamProxy = () => {
|
|
1144
|
+
const target = {};
|
|
1145
|
+
return new Proxy(target, {
|
|
1146
|
+
get(t, prop, receiver) {
|
|
1147
|
+
if (prop === "then") return void 0;
|
|
1148
|
+
if (typeof prop === "symbol") {
|
|
1149
|
+
return Reflect.get(t, prop, receiver);
|
|
1150
|
+
}
|
|
1151
|
+
if (typeof prop === "string" && prop.startsWith("$")) {
|
|
1152
|
+
return buildParamProxy(prop.slice(1));
|
|
1153
|
+
}
|
|
1154
|
+
if (prop in t) {
|
|
1155
|
+
return t[prop];
|
|
1156
|
+
}
|
|
1157
|
+
return buildParamProxy(String(prop));
|
|
1158
|
+
}
|
|
1159
|
+
});
|
|
1160
|
+
};
|
|
1161
|
+
|
|
1115
1162
|
// src/core/ast/adapters.ts
|
|
1116
1163
|
var hasAlias = (obj) => typeof obj === "object" && obj !== null && "alias" in obj;
|
|
1117
1164
|
var toColumnRef = (col2) => ({
|
|
@@ -1821,6 +1868,7 @@ var Dialect = class _Dialect {
|
|
|
1821
1868
|
}
|
|
1822
1869
|
registerDefaultOperandCompilers() {
|
|
1823
1870
|
this.registerOperandCompiler("Literal", (literal, ctx) => ctx.addParameter(literal.value));
|
|
1871
|
+
this.registerOperandCompiler("Param", (_param, ctx) => ctx.addParameter(null));
|
|
1824
1872
|
this.registerOperandCompiler("AliasRef", (alias, _ctx) => {
|
|
1825
1873
|
void _ctx;
|
|
1826
1874
|
return this.quoteIdentifier(alias.name);
|
|
@@ -4344,6 +4392,7 @@ var collectFromOperand = (node, collector) => {
|
|
|
4344
4392
|
break;
|
|
4345
4393
|
case "Literal":
|
|
4346
4394
|
case "AliasRef":
|
|
4395
|
+
case "Param":
|
|
4347
4396
|
break;
|
|
4348
4397
|
default:
|
|
4349
4398
|
break;
|
|
@@ -6950,26 +6999,33 @@ var SelectRelationFacet = class {
|
|
|
6950
6999
|
};
|
|
6951
7000
|
|
|
6952
7001
|
// src/openapi/type-mappers.ts
|
|
6953
|
-
var mapColumnType = (column) => {
|
|
7002
|
+
var mapColumnType = (column, options = {}) => {
|
|
7003
|
+
const resolved = resolveColumnOptions(options);
|
|
6954
7004
|
const sqlType = normalizeType(column.type);
|
|
6955
7005
|
const baseSchema = mapSqlTypeToBaseSchema(sqlType, column);
|
|
6956
7006
|
const schema = {
|
|
6957
|
-
...baseSchema
|
|
6958
|
-
description: column.comment,
|
|
6959
|
-
nullable: !column.notNull && !column.primary
|
|
7007
|
+
...baseSchema
|
|
6960
7008
|
};
|
|
6961
|
-
if (
|
|
7009
|
+
if (resolved.includeDescriptions && column.comment) {
|
|
7010
|
+
schema.description = column.comment;
|
|
7011
|
+
}
|
|
7012
|
+
if (resolved.includeNullable) {
|
|
7013
|
+
schema.nullable = !column.notNull && !column.primary;
|
|
7014
|
+
}
|
|
7015
|
+
if ((sqlType === "varchar" || sqlType === "char") && column.args) {
|
|
6962
7016
|
schema.maxLength = column.args[0];
|
|
6963
7017
|
}
|
|
6964
|
-
if (
|
|
7018
|
+
if ((sqlType === "decimal" || sqlType === "float") && column.args) {
|
|
6965
7019
|
if (column.args.length >= 1) {
|
|
6966
7020
|
schema.minimum = -(10 ** column.args[0]);
|
|
6967
7021
|
}
|
|
6968
7022
|
}
|
|
6969
|
-
if (
|
|
7023
|
+
if (!resolved.includeEnums) {
|
|
7024
|
+
delete schema.enum;
|
|
7025
|
+
} else if (sqlType === "enum" && column.args && column.args.length > 0) {
|
|
6970
7026
|
schema.enum = column.args;
|
|
6971
7027
|
}
|
|
6972
|
-
if (column.default !== void 0) {
|
|
7028
|
+
if (resolved.includeDefaults && column.default !== void 0) {
|
|
6973
7029
|
schema.default = column.default;
|
|
6974
7030
|
}
|
|
6975
7031
|
return schema;
|
|
@@ -7085,6 +7141,13 @@ var inferTypeFromTsType = (tsType) => {
|
|
|
7085
7141
|
}
|
|
7086
7142
|
return "string";
|
|
7087
7143
|
};
|
|
7144
|
+
var resolveColumnOptions = (options) => ({
|
|
7145
|
+
includeDescriptions: options.includeDescriptions ?? false,
|
|
7146
|
+
includeEnums: options.includeEnums ?? true,
|
|
7147
|
+
includeExamples: options.includeExamples ?? false,
|
|
7148
|
+
includeDefaults: options.includeDefaults ?? true,
|
|
7149
|
+
includeNullable: options.includeNullable ?? true
|
|
7150
|
+
});
|
|
7088
7151
|
var mapRelationType = (relationType) => {
|
|
7089
7152
|
switch (relationType) {
|
|
7090
7153
|
case "HAS_MANY":
|
|
@@ -7112,14 +7175,55 @@ var getTemporalFormat = (sqlType) => {
|
|
|
7112
7175
|
};
|
|
7113
7176
|
|
|
7114
7177
|
// src/openapi/schema-extractor.ts
|
|
7178
|
+
var DEFAULT_MAX_DEPTH = 5;
|
|
7115
7179
|
var extractSchema = (table, plan, projectionNodes, options = {}) => {
|
|
7116
|
-
const
|
|
7117
|
-
const
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
7180
|
+
const outputOptions = resolveOutputOptions(options);
|
|
7181
|
+
const outputContext = createContext(outputOptions.maxDepth ?? DEFAULT_MAX_DEPTH);
|
|
7182
|
+
const output = extractOutputSchema(table, plan, projectionNodes, outputContext, outputOptions);
|
|
7183
|
+
const inputOptions = resolveInputOptions(options);
|
|
7184
|
+
if (!inputOptions) {
|
|
7185
|
+
return { output };
|
|
7186
|
+
}
|
|
7187
|
+
const inputContext = createContext(inputOptions.maxDepth ?? DEFAULT_MAX_DEPTH);
|
|
7188
|
+
const input = extractInputSchema(table, inputContext, inputOptions);
|
|
7189
|
+
return { output, input };
|
|
7190
|
+
};
|
|
7191
|
+
var resolveOutputOptions = (options) => ({
|
|
7192
|
+
mode: options.mode ?? "full",
|
|
7193
|
+
includeDescriptions: options.includeDescriptions,
|
|
7194
|
+
includeEnums: options.includeEnums,
|
|
7195
|
+
includeExamples: options.includeExamples,
|
|
7196
|
+
includeDefaults: options.includeDefaults,
|
|
7197
|
+
includeNullable: options.includeNullable,
|
|
7198
|
+
maxDepth: options.maxDepth ?? DEFAULT_MAX_DEPTH
|
|
7199
|
+
});
|
|
7200
|
+
var resolveInputOptions = (options) => {
|
|
7201
|
+
if (options.input === false) return void 0;
|
|
7202
|
+
const input = options.input ?? {};
|
|
7203
|
+
const mode = input.mode ?? "create";
|
|
7204
|
+
return {
|
|
7205
|
+
mode,
|
|
7206
|
+
includeRelations: input.includeRelations ?? true,
|
|
7207
|
+
relationMode: input.relationMode ?? "mixed",
|
|
7208
|
+
includeDescriptions: input.includeDescriptions ?? options.includeDescriptions,
|
|
7209
|
+
includeEnums: input.includeEnums ?? options.includeEnums,
|
|
7210
|
+
includeExamples: input.includeExamples ?? options.includeExamples,
|
|
7211
|
+
includeDefaults: input.includeDefaults ?? options.includeDefaults,
|
|
7212
|
+
includeNullable: input.includeNullable ?? options.includeNullable,
|
|
7213
|
+
maxDepth: input.maxDepth ?? options.maxDepth ?? DEFAULT_MAX_DEPTH,
|
|
7214
|
+
omitReadOnly: input.omitReadOnly ?? true,
|
|
7215
|
+
excludePrimaryKey: input.excludePrimaryKey ?? false,
|
|
7216
|
+
requirePrimaryKey: input.requirePrimaryKey ?? mode === "update"
|
|
7122
7217
|
};
|
|
7218
|
+
};
|
|
7219
|
+
var createContext = (maxDepth) => ({
|
|
7220
|
+
visitedTables: /* @__PURE__ */ new Set(),
|
|
7221
|
+
schemaCache: /* @__PURE__ */ new Map(),
|
|
7222
|
+
depth: 0,
|
|
7223
|
+
maxDepth
|
|
7224
|
+
});
|
|
7225
|
+
var extractOutputSchema = (table, plan, projectionNodes, context, options) => {
|
|
7226
|
+
const mode = options.mode ?? "full";
|
|
7123
7227
|
const hasComputedFields = projectionNodes && projectionNodes.some(
|
|
7124
7228
|
(node) => node.type !== "Column"
|
|
7125
7229
|
);
|
|
@@ -7131,9 +7235,97 @@ var extractSchema = (table, plan, projectionNodes, options = {}) => {
|
|
|
7131
7235
|
}
|
|
7132
7236
|
return extractFullTableSchema(table, context, options);
|
|
7133
7237
|
};
|
|
7238
|
+
var extractInputSchema = (table, context, options) => {
|
|
7239
|
+
const cacheKey = `${table.name}:${options.mode ?? "create"}`;
|
|
7240
|
+
if (context.schemaCache.has(cacheKey)) {
|
|
7241
|
+
return context.schemaCache.get(cacheKey);
|
|
7242
|
+
}
|
|
7243
|
+
if (context.visitedTables.has(cacheKey) && context.depth > 0) {
|
|
7244
|
+
return buildCircularReferenceSchema(table.name, "input");
|
|
7245
|
+
}
|
|
7246
|
+
context.visitedTables.add(cacheKey);
|
|
7247
|
+
const properties = {};
|
|
7248
|
+
const required = [];
|
|
7249
|
+
const primaryKey = findPrimaryKey(table);
|
|
7250
|
+
for (const [columnName, column] of Object.entries(table.columns)) {
|
|
7251
|
+
const isPrimary = columnName === primaryKey || column.primary;
|
|
7252
|
+
if (options.excludePrimaryKey && isPrimary) continue;
|
|
7253
|
+
if (options.omitReadOnly && isReadOnlyColumn(column)) continue;
|
|
7254
|
+
properties[columnName] = mapColumnType(column, options);
|
|
7255
|
+
if (options.mode === "create" && isRequiredForCreate(column)) {
|
|
7256
|
+
required.push(columnName);
|
|
7257
|
+
}
|
|
7258
|
+
if (options.mode === "update" && options.requirePrimaryKey && isPrimary) {
|
|
7259
|
+
required.push(columnName);
|
|
7260
|
+
}
|
|
7261
|
+
}
|
|
7262
|
+
if (options.includeRelations && context.depth < context.maxDepth) {
|
|
7263
|
+
for (const [relationName, relation] of Object.entries(table.relations)) {
|
|
7264
|
+
properties[relationName] = extractInputRelationSchema(
|
|
7265
|
+
relation,
|
|
7266
|
+
{ ...context, depth: context.depth + 1 },
|
|
7267
|
+
options
|
|
7268
|
+
);
|
|
7269
|
+
}
|
|
7270
|
+
}
|
|
7271
|
+
const schema = {
|
|
7272
|
+
type: "object",
|
|
7273
|
+
properties,
|
|
7274
|
+
required
|
|
7275
|
+
};
|
|
7276
|
+
context.schemaCache.set(cacheKey, schema);
|
|
7277
|
+
return schema;
|
|
7278
|
+
};
|
|
7279
|
+
var isReadOnlyColumn = (column) => Boolean(column.autoIncrement || column.generated === "always");
|
|
7280
|
+
var isRequiredForCreate = (column) => {
|
|
7281
|
+
if (isReadOnlyColumn(column)) return false;
|
|
7282
|
+
if (column.default !== void 0) return false;
|
|
7283
|
+
return Boolean(column.notNull || column.primary);
|
|
7284
|
+
};
|
|
7285
|
+
var buildPrimaryKeySchema = (table, options) => {
|
|
7286
|
+
const primaryKey = findPrimaryKey(table);
|
|
7287
|
+
const column = table.columns[primaryKey];
|
|
7288
|
+
if (!column) {
|
|
7289
|
+
return {
|
|
7290
|
+
anyOf: [
|
|
7291
|
+
{ type: "string" },
|
|
7292
|
+
{ type: "number" },
|
|
7293
|
+
{ type: "integer" }
|
|
7294
|
+
]
|
|
7295
|
+
};
|
|
7296
|
+
}
|
|
7297
|
+
return mapColumnType(column, options);
|
|
7298
|
+
};
|
|
7299
|
+
var extractInputRelationSchema = (relation, context, options) => {
|
|
7300
|
+
const { type: relationType, isNullable } = mapRelationType(relation.type);
|
|
7301
|
+
const relationMode = options.relationMode ?? "mixed";
|
|
7302
|
+
const allowIds = relationMode !== "objects";
|
|
7303
|
+
const allowObjects = relationMode !== "ids";
|
|
7304
|
+
const variants = [];
|
|
7305
|
+
if (allowIds) {
|
|
7306
|
+
variants.push(buildPrimaryKeySchema(relation.target, options));
|
|
7307
|
+
}
|
|
7308
|
+
if (allowObjects) {
|
|
7309
|
+
const targetSchema = extractInputSchema(relation.target, context, options);
|
|
7310
|
+
variants.push(targetSchema);
|
|
7311
|
+
}
|
|
7312
|
+
const itemSchema = variants.length === 1 ? variants[0] : { anyOf: variants };
|
|
7313
|
+
if (relationType === "array") {
|
|
7314
|
+
return {
|
|
7315
|
+
type: "array",
|
|
7316
|
+
items: itemSchema,
|
|
7317
|
+
nullable: isNullable
|
|
7318
|
+
};
|
|
7319
|
+
}
|
|
7320
|
+
return {
|
|
7321
|
+
...itemSchema,
|
|
7322
|
+
nullable: isNullable
|
|
7323
|
+
};
|
|
7324
|
+
};
|
|
7134
7325
|
var extractFromProjectionNodes = (table, projectionNodes, context, options) => {
|
|
7135
7326
|
const properties = {};
|
|
7136
7327
|
const required = [];
|
|
7328
|
+
const includeDescriptions = Boolean(options.includeDescriptions);
|
|
7137
7329
|
for (const node of projectionNodes) {
|
|
7138
7330
|
if (!node || typeof node !== "object") continue;
|
|
7139
7331
|
const projection = node;
|
|
@@ -7143,10 +7335,7 @@ var extractFromProjectionNodes = (table, projectionNodes, context, options) => {
|
|
|
7143
7335
|
const columnNode4 = node;
|
|
7144
7336
|
const column = table.columns[columnNode4.name];
|
|
7145
7337
|
if (!column) continue;
|
|
7146
|
-
const property = mapColumnType(column);
|
|
7147
|
-
if (!property.description && options.includeDescriptions && column.comment) {
|
|
7148
|
-
property.description = column.comment;
|
|
7149
|
-
}
|
|
7338
|
+
const property = mapColumnType(column, options);
|
|
7150
7339
|
properties[propertyName] = property;
|
|
7151
7340
|
if (column.notNull || column.primary) {
|
|
7152
7341
|
required.push(propertyName);
|
|
@@ -7154,7 +7343,7 @@ var extractFromProjectionNodes = (table, projectionNodes, context, options) => {
|
|
|
7154
7343
|
} else if (projection.type === "Function" || projection.type === "WindowFunction") {
|
|
7155
7344
|
const fnNode = node;
|
|
7156
7345
|
const functionName = fnNode.fn?.toUpperCase() ?? fnNode.name?.toUpperCase() ?? "";
|
|
7157
|
-
const propertySchema = projection.type === "Function" ? mapFunctionNodeToSchema(functionName) : mapWindowFunctionToSchema(functionName);
|
|
7346
|
+
const propertySchema = projection.type === "Function" ? mapFunctionNodeToSchema(functionName, includeDescriptions) : mapWindowFunctionToSchema(functionName, includeDescriptions);
|
|
7158
7347
|
properties[propertyName] = propertySchema;
|
|
7159
7348
|
const isCountFunction = functionName === "COUNT";
|
|
7160
7349
|
const isWindowRankFunction = functionName === "ROW_NUMBER" || functionName === "RANK";
|
|
@@ -7164,23 +7353,29 @@ var extractFromProjectionNodes = (table, projectionNodes, context, options) => {
|
|
|
7164
7353
|
} else if (projection.type === "CaseExpression") {
|
|
7165
7354
|
const propertySchema = {
|
|
7166
7355
|
type: "string",
|
|
7167
|
-
description: "Computed CASE expression",
|
|
7168
7356
|
nullable: true
|
|
7169
7357
|
};
|
|
7358
|
+
if (includeDescriptions) {
|
|
7359
|
+
propertySchema.description = "Computed CASE expression";
|
|
7360
|
+
}
|
|
7170
7361
|
properties[propertyName] = propertySchema;
|
|
7171
7362
|
} else if (projection.type === "ScalarSubquery") {
|
|
7172
7363
|
const propertySchema = {
|
|
7173
7364
|
type: "object",
|
|
7174
|
-
description: "Subquery result",
|
|
7175
7365
|
nullable: true
|
|
7176
7366
|
};
|
|
7367
|
+
if (includeDescriptions) {
|
|
7368
|
+
propertySchema.description = "Subquery result";
|
|
7369
|
+
}
|
|
7177
7370
|
properties[propertyName] = propertySchema;
|
|
7178
7371
|
} else if (projection.type === "CastExpression") {
|
|
7179
7372
|
const propertySchema = {
|
|
7180
7373
|
type: "string",
|
|
7181
|
-
description: "CAST expression result",
|
|
7182
7374
|
nullable: true
|
|
7183
7375
|
};
|
|
7376
|
+
if (includeDescriptions) {
|
|
7377
|
+
propertySchema.description = "CAST expression result";
|
|
7378
|
+
}
|
|
7184
7379
|
properties[propertyName] = propertySchema;
|
|
7185
7380
|
}
|
|
7186
7381
|
}
|
|
@@ -7190,7 +7385,7 @@ var extractFromProjectionNodes = (table, projectionNodes, context, options) => {
|
|
|
7190
7385
|
required
|
|
7191
7386
|
};
|
|
7192
7387
|
};
|
|
7193
|
-
var mapFunctionNodeToSchema = (functionName) => {
|
|
7388
|
+
var mapFunctionNodeToSchema = (functionName, includeDescriptions) => {
|
|
7194
7389
|
const upperName = functionName.toUpperCase();
|
|
7195
7390
|
switch (upperName) {
|
|
7196
7391
|
case "COUNT":
|
|
@@ -7198,74 +7393,69 @@ var mapFunctionNodeToSchema = (functionName) => {
|
|
|
7198
7393
|
case "AVG":
|
|
7199
7394
|
case "MIN":
|
|
7200
7395
|
case "MAX":
|
|
7201
|
-
return {
|
|
7396
|
+
return withOptionalDescription({
|
|
7202
7397
|
type: "number",
|
|
7203
|
-
description: `${upperName} aggregate function result`,
|
|
7204
7398
|
nullable: false
|
|
7205
|
-
};
|
|
7399
|
+
}, includeDescriptions, `${upperName} aggregate function result`);
|
|
7206
7400
|
case "GROUP_CONCAT":
|
|
7207
7401
|
case "STRING_AGG":
|
|
7208
7402
|
case "ARRAY_AGG":
|
|
7209
|
-
return {
|
|
7403
|
+
return withOptionalDescription({
|
|
7210
7404
|
type: "string",
|
|
7211
|
-
description: `${upperName} aggregate function result`,
|
|
7212
7405
|
nullable: true
|
|
7213
|
-
};
|
|
7406
|
+
}, includeDescriptions, `${upperName} aggregate function result`);
|
|
7214
7407
|
case "JSON_ARRAYAGG":
|
|
7215
7408
|
case "JSON_OBJECTAGG":
|
|
7216
|
-
return {
|
|
7409
|
+
return withOptionalDescription({
|
|
7217
7410
|
type: "object",
|
|
7218
|
-
description: `${upperName} aggregate function result`,
|
|
7219
7411
|
nullable: true
|
|
7220
|
-
};
|
|
7412
|
+
}, includeDescriptions, `${upperName} aggregate function result`);
|
|
7221
7413
|
default:
|
|
7222
|
-
return {
|
|
7414
|
+
return withOptionalDescription({
|
|
7223
7415
|
type: "string",
|
|
7224
|
-
description: `Unknown function: ${functionName}`,
|
|
7225
7416
|
nullable: true
|
|
7226
|
-
};
|
|
7417
|
+
}, includeDescriptions, `Unknown function: ${functionName}`);
|
|
7227
7418
|
}
|
|
7228
7419
|
};
|
|
7229
|
-
var mapWindowFunctionToSchema = (functionName) => {
|
|
7420
|
+
var mapWindowFunctionToSchema = (functionName, includeDescriptions) => {
|
|
7230
7421
|
const upperName = functionName.toUpperCase();
|
|
7231
7422
|
switch (upperName) {
|
|
7232
7423
|
case "ROW_NUMBER":
|
|
7233
7424
|
case "RANK":
|
|
7234
7425
|
case "DENSE_RANK":
|
|
7235
7426
|
case "NTILE":
|
|
7236
|
-
return {
|
|
7427
|
+
return withOptionalDescription({
|
|
7237
7428
|
type: "integer",
|
|
7238
|
-
description: `${upperName} window function result`,
|
|
7239
7429
|
nullable: false
|
|
7240
|
-
};
|
|
7430
|
+
}, includeDescriptions, `${upperName} window function result`);
|
|
7241
7431
|
case "LAG":
|
|
7242
7432
|
case "LEAD":
|
|
7243
7433
|
case "FIRST_VALUE":
|
|
7244
7434
|
case "LAST_VALUE":
|
|
7245
|
-
return {
|
|
7435
|
+
return withOptionalDescription({
|
|
7246
7436
|
type: "string",
|
|
7247
|
-
description: `${upperName} window function result`,
|
|
7248
7437
|
nullable: true
|
|
7249
|
-
};
|
|
7438
|
+
}, includeDescriptions, `${upperName} window function result`);
|
|
7250
7439
|
default:
|
|
7251
|
-
return {
|
|
7440
|
+
return withOptionalDescription({
|
|
7252
7441
|
type: "string",
|
|
7253
|
-
description: `Unknown window function: ${functionName}`,
|
|
7254
7442
|
nullable: true
|
|
7255
|
-
};
|
|
7443
|
+
}, includeDescriptions, `Unknown window function: ${functionName}`);
|
|
7256
7444
|
}
|
|
7257
7445
|
};
|
|
7446
|
+
var withOptionalDescription = (schema, includeDescriptions, description) => {
|
|
7447
|
+
if (includeDescriptions) {
|
|
7448
|
+
return { ...schema, description };
|
|
7449
|
+
}
|
|
7450
|
+
return schema;
|
|
7451
|
+
};
|
|
7258
7452
|
var extractSelectedSchema = (table, plan, context, options) => {
|
|
7259
7453
|
const properties = {};
|
|
7260
7454
|
const required = [];
|
|
7261
7455
|
plan.rootColumns.forEach((columnName) => {
|
|
7262
7456
|
const column = table.columns[columnName];
|
|
7263
7457
|
if (!column) return;
|
|
7264
|
-
|
|
7265
|
-
if (!property.description && options.includeDescriptions && column.comment) {
|
|
7266
|
-
property.description = column.comment;
|
|
7267
|
-
}
|
|
7268
|
-
properties[columnName] = property;
|
|
7458
|
+
properties[columnName] = mapColumnType(column, options);
|
|
7269
7459
|
if (column.notNull || column.primary) {
|
|
7270
7460
|
required.push(columnName);
|
|
7271
7461
|
}
|
|
@@ -7298,26 +7488,13 @@ var extractFullTableSchema = (table, context, options) => {
|
|
|
7298
7488
|
return context.schemaCache.get(cacheKey);
|
|
7299
7489
|
}
|
|
7300
7490
|
if (context.visitedTables.has(cacheKey) && context.depth > 0) {
|
|
7301
|
-
return
|
|
7302
|
-
type: "object",
|
|
7303
|
-
properties: {
|
|
7304
|
-
_ref: {
|
|
7305
|
-
type: "string",
|
|
7306
|
-
description: `Circular reference to ${table.name}`
|
|
7307
|
-
}
|
|
7308
|
-
},
|
|
7309
|
-
required: []
|
|
7310
|
-
};
|
|
7491
|
+
return buildCircularReferenceSchema(table.name, "output");
|
|
7311
7492
|
}
|
|
7312
7493
|
context.visitedTables.add(cacheKey);
|
|
7313
7494
|
const properties = {};
|
|
7314
7495
|
const required = [];
|
|
7315
7496
|
Object.entries(table.columns).forEach(([columnName, column]) => {
|
|
7316
|
-
|
|
7317
|
-
if (!property.description && options.includeDescriptions && column.comment) {
|
|
7318
|
-
property.description = column.comment;
|
|
7319
|
-
}
|
|
7320
|
-
properties[columnName] = property;
|
|
7497
|
+
properties[columnName] = mapColumnType(column, options);
|
|
7321
7498
|
if (column.notNull || column.primary) {
|
|
7322
7499
|
required.push(columnName);
|
|
7323
7500
|
}
|
|
@@ -7377,10 +7554,189 @@ var extractRelationSchema = (relation, relationPlan, selectedColumns, context, o
|
|
|
7377
7554
|
description: targetSchema.description
|
|
7378
7555
|
};
|
|
7379
7556
|
};
|
|
7557
|
+
var buildCircularReferenceSchema = (tableName, kind) => ({
|
|
7558
|
+
type: "object",
|
|
7559
|
+
properties: {
|
|
7560
|
+
_ref: {
|
|
7561
|
+
type: "string",
|
|
7562
|
+
description: `Circular ${kind} reference to ${tableName}`
|
|
7563
|
+
}
|
|
7564
|
+
},
|
|
7565
|
+
required: []
|
|
7566
|
+
});
|
|
7380
7567
|
var schemaToJson = (schema, pretty = false) => {
|
|
7381
7568
|
return JSON.stringify(schema, null, pretty ? 2 : 0);
|
|
7382
7569
|
};
|
|
7383
7570
|
|
|
7571
|
+
// src/openapi/query-parameters.ts
|
|
7572
|
+
var FILTER_PARAM_NAME = "filter";
|
|
7573
|
+
var buildRootTableNames = (table, from) => {
|
|
7574
|
+
const names = /* @__PURE__ */ new Set([table.name]);
|
|
7575
|
+
if (from?.type === "Table") {
|
|
7576
|
+
names.add(from.name);
|
|
7577
|
+
if (from.alias) names.add(from.alias);
|
|
7578
|
+
}
|
|
7579
|
+
return names;
|
|
7580
|
+
};
|
|
7581
|
+
var collectFilterColumns = (expr, table, rootTables) => {
|
|
7582
|
+
const columns = /* @__PURE__ */ new Set();
|
|
7583
|
+
const recordColumn = (node) => {
|
|
7584
|
+
if (!rootTables.has(node.table)) return;
|
|
7585
|
+
if (node.name in table.columns) {
|
|
7586
|
+
columns.add(node.name);
|
|
7587
|
+
}
|
|
7588
|
+
};
|
|
7589
|
+
const visitOrderingTerm = (term) => {
|
|
7590
|
+
if (!term || typeof term !== "object") return;
|
|
7591
|
+
if (isOperandNode(term)) {
|
|
7592
|
+
visitOperand2(term);
|
|
7593
|
+
return;
|
|
7594
|
+
}
|
|
7595
|
+
if ("type" in term) {
|
|
7596
|
+
visitExpression2(term);
|
|
7597
|
+
}
|
|
7598
|
+
};
|
|
7599
|
+
const visitOrderBy = (orderBy) => {
|
|
7600
|
+
if (!orderBy) return;
|
|
7601
|
+
orderBy.forEach((node) => visitOrderingTerm(node.term));
|
|
7602
|
+
};
|
|
7603
|
+
const visitOperand2 = (node) => {
|
|
7604
|
+
switch (node.type) {
|
|
7605
|
+
case "Column":
|
|
7606
|
+
recordColumn(node);
|
|
7607
|
+
return;
|
|
7608
|
+
case "Function": {
|
|
7609
|
+
const fn8 = node;
|
|
7610
|
+
fn8.args?.forEach(visitOperand2);
|
|
7611
|
+
visitOrderBy(fn8.orderBy);
|
|
7612
|
+
if (fn8.separator) visitOperand2(fn8.separator);
|
|
7613
|
+
return;
|
|
7614
|
+
}
|
|
7615
|
+
case "JsonPath": {
|
|
7616
|
+
const jp = node;
|
|
7617
|
+
recordColumn(jp.column);
|
|
7618
|
+
return;
|
|
7619
|
+
}
|
|
7620
|
+
case "ScalarSubquery":
|
|
7621
|
+
return;
|
|
7622
|
+
case "CaseExpression": {
|
|
7623
|
+
const cs = node;
|
|
7624
|
+
cs.conditions.forEach((condition) => {
|
|
7625
|
+
visitExpression2(condition.when);
|
|
7626
|
+
visitOperand2(condition.then);
|
|
7627
|
+
});
|
|
7628
|
+
if (cs.else) visitOperand2(cs.else);
|
|
7629
|
+
return;
|
|
7630
|
+
}
|
|
7631
|
+
case "Cast": {
|
|
7632
|
+
const cast2 = node;
|
|
7633
|
+
visitOperand2(cast2.expression);
|
|
7634
|
+
return;
|
|
7635
|
+
}
|
|
7636
|
+
case "WindowFunction": {
|
|
7637
|
+
const windowFn = node;
|
|
7638
|
+
windowFn.args?.forEach(visitOperand2);
|
|
7639
|
+
windowFn.partitionBy?.forEach(recordColumn);
|
|
7640
|
+
visitOrderBy(windowFn.orderBy);
|
|
7641
|
+
return;
|
|
7642
|
+
}
|
|
7643
|
+
case "ArithmeticExpression": {
|
|
7644
|
+
const arith = node;
|
|
7645
|
+
visitOperand2(arith.left);
|
|
7646
|
+
visitOperand2(arith.right);
|
|
7647
|
+
return;
|
|
7648
|
+
}
|
|
7649
|
+
case "BitwiseExpression": {
|
|
7650
|
+
const bitwise = node;
|
|
7651
|
+
visitOperand2(bitwise.left);
|
|
7652
|
+
visitOperand2(bitwise.right);
|
|
7653
|
+
return;
|
|
7654
|
+
}
|
|
7655
|
+
case "Collate": {
|
|
7656
|
+
const collate2 = node;
|
|
7657
|
+
visitOperand2(collate2.expression);
|
|
7658
|
+
return;
|
|
7659
|
+
}
|
|
7660
|
+
case "AliasRef":
|
|
7661
|
+
case "Literal":
|
|
7662
|
+
case "Param":
|
|
7663
|
+
return;
|
|
7664
|
+
default:
|
|
7665
|
+
return;
|
|
7666
|
+
}
|
|
7667
|
+
};
|
|
7668
|
+
const visitExpression2 = (node) => {
|
|
7669
|
+
switch (node.type) {
|
|
7670
|
+
case "BinaryExpression":
|
|
7671
|
+
visitOperand2(node.left);
|
|
7672
|
+
visitOperand2(node.right);
|
|
7673
|
+
if (node.escape) visitOperand2(node.escape);
|
|
7674
|
+
return;
|
|
7675
|
+
case "LogicalExpression":
|
|
7676
|
+
node.operands.forEach(visitExpression2);
|
|
7677
|
+
return;
|
|
7678
|
+
case "NullExpression":
|
|
7679
|
+
visitOperand2(node.left);
|
|
7680
|
+
return;
|
|
7681
|
+
case "InExpression":
|
|
7682
|
+
visitOperand2(node.left);
|
|
7683
|
+
if (Array.isArray(node.right)) {
|
|
7684
|
+
node.right.forEach(visitOperand2);
|
|
7685
|
+
}
|
|
7686
|
+
return;
|
|
7687
|
+
case "ExistsExpression":
|
|
7688
|
+
return;
|
|
7689
|
+
case "BetweenExpression":
|
|
7690
|
+
visitOperand2(node.left);
|
|
7691
|
+
visitOperand2(node.lower);
|
|
7692
|
+
visitOperand2(node.upper);
|
|
7693
|
+
return;
|
|
7694
|
+
case "ArithmeticExpression":
|
|
7695
|
+
visitOperand2(node.left);
|
|
7696
|
+
visitOperand2(node.right);
|
|
7697
|
+
return;
|
|
7698
|
+
case "BitwiseExpression":
|
|
7699
|
+
visitOperand2(node.left);
|
|
7700
|
+
visitOperand2(node.right);
|
|
7701
|
+
return;
|
|
7702
|
+
default:
|
|
7703
|
+
return;
|
|
7704
|
+
}
|
|
7705
|
+
};
|
|
7706
|
+
visitExpression2(expr);
|
|
7707
|
+
return columns;
|
|
7708
|
+
};
|
|
7709
|
+
var buildFilterParameters = (table, where, from, options = {}) => {
|
|
7710
|
+
if (!where) return [];
|
|
7711
|
+
const rootTables = buildRootTableNames(table, from);
|
|
7712
|
+
const columnNames = collectFilterColumns(where, table, rootTables);
|
|
7713
|
+
let schema;
|
|
7714
|
+
if (columnNames.size) {
|
|
7715
|
+
const properties = {};
|
|
7716
|
+
for (const name of columnNames) {
|
|
7717
|
+
const column = table.columns[name];
|
|
7718
|
+
if (!column) continue;
|
|
7719
|
+
properties[name] = mapColumnType(column, options);
|
|
7720
|
+
}
|
|
7721
|
+
schema = {
|
|
7722
|
+
type: "object",
|
|
7723
|
+
properties
|
|
7724
|
+
};
|
|
7725
|
+
} else {
|
|
7726
|
+
schema = {
|
|
7727
|
+
type: "object",
|
|
7728
|
+
additionalProperties: true
|
|
7729
|
+
};
|
|
7730
|
+
}
|
|
7731
|
+
return [{
|
|
7732
|
+
name: FILTER_PARAM_NAME,
|
|
7733
|
+
in: "query",
|
|
7734
|
+
style: "deepObject",
|
|
7735
|
+
explode: true,
|
|
7736
|
+
schema
|
|
7737
|
+
}];
|
|
7738
|
+
};
|
|
7739
|
+
|
|
7384
7740
|
// src/query-builder/select.ts
|
|
7385
7741
|
var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
7386
7742
|
env;
|
|
@@ -8200,16 +8556,26 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
8200
8556
|
return this.context.hydration.getPlan();
|
|
8201
8557
|
}
|
|
8202
8558
|
/**
|
|
8203
|
-
* Gets OpenAPI 3.1 JSON
|
|
8559
|
+
* Gets OpenAPI 3.1 JSON Schemas for query output and optional input payloads
|
|
8204
8560
|
* @param options - Schema generation options
|
|
8205
|
-
* @returns OpenAPI 3.1 JSON
|
|
8561
|
+
* @returns OpenAPI 3.1 JSON Schemas for query output and input payloads
|
|
8206
8562
|
* @example
|
|
8207
|
-
* const
|
|
8208
|
-
* console.log(JSON.stringify(
|
|
8563
|
+
* const { output } = qb.select('id', 'title', 'author').getSchema();
|
|
8564
|
+
* console.log(JSON.stringify(output, null, 2));
|
|
8209
8565
|
*/
|
|
8210
8566
|
getSchema(options) {
|
|
8211
8567
|
const plan = this.context.hydration.getPlan();
|
|
8212
|
-
|
|
8568
|
+
const bundle = extractSchema(this.env.table, plan, this.context.state.ast.columns, options);
|
|
8569
|
+
const parameters = buildFilterParameters(
|
|
8570
|
+
this.env.table,
|
|
8571
|
+
this.context.state.ast.where,
|
|
8572
|
+
this.context.state.ast.from,
|
|
8573
|
+
options ?? {}
|
|
8574
|
+
);
|
|
8575
|
+
if (parameters.length) {
|
|
8576
|
+
return { ...bundle, parameters };
|
|
8577
|
+
}
|
|
8578
|
+
return bundle;
|
|
8213
8579
|
}
|
|
8214
8580
|
/**
|
|
8215
8581
|
* Gets the Abstract Syntax Tree (AST) representation of the query
|
|
@@ -11333,6 +11699,7 @@ var TypeScriptGenerator = class {
|
|
|
11333
11699
|
case "WindowFunction":
|
|
11334
11700
|
case "Cast":
|
|
11335
11701
|
case "Collate":
|
|
11702
|
+
case "Param":
|
|
11336
11703
|
return this.printOperand(term);
|
|
11337
11704
|
default:
|
|
11338
11705
|
return this.printExpression(term);
|
|
@@ -11377,6 +11744,9 @@ var TypeScriptGenerator = class {
|
|
|
11377
11744
|
visitLiteral(node) {
|
|
11378
11745
|
return this.printLiteralOperand(node);
|
|
11379
11746
|
}
|
|
11747
|
+
visitParam(node) {
|
|
11748
|
+
return this.printParamOperand(node);
|
|
11749
|
+
}
|
|
11380
11750
|
visitFunction(node) {
|
|
11381
11751
|
return this.printFunctionOperand(node);
|
|
11382
11752
|
}
|
|
@@ -11498,6 +11868,10 @@ var TypeScriptGenerator = class {
|
|
|
11498
11868
|
if (literal.value === null) return "null";
|
|
11499
11869
|
return typeof literal.value === "string" ? `'${literal.value}'` : String(literal.value);
|
|
11500
11870
|
}
|
|
11871
|
+
printParamOperand(param) {
|
|
11872
|
+
const name = param.name.replace(/'/g, "\\'");
|
|
11873
|
+
return `{ type: 'Param', name: '${name}' }`;
|
|
11874
|
+
}
|
|
11501
11875
|
/**
|
|
11502
11876
|
* Prints a function operand to TypeScript code
|
|
11503
11877
|
* @param fn - Function node
|
|
@@ -11826,6 +12200,7 @@ var UnitOfWork = class {
|
|
|
11826
12200
|
const compiled = builder.compile(this.dialect);
|
|
11827
12201
|
const results = await this.executeCompiled(compiled);
|
|
11828
12202
|
this.applyReturningResults(tracked, results);
|
|
12203
|
+
this.applyInsertId(tracked, results);
|
|
11829
12204
|
tracked.status = "managed" /* Managed */;
|
|
11830
12205
|
tracked.original = this.createSnapshot(tracked.table, tracked.entity);
|
|
11831
12206
|
tracked.pk = this.getPrimaryKeyValue(tracked);
|
|
@@ -11951,6 +12326,16 @@ var UnitOfWork = class {
|
|
|
11951
12326
|
tracked.entity[columnName] = row[i];
|
|
11952
12327
|
}
|
|
11953
12328
|
}
|
|
12329
|
+
applyInsertId(tracked, results) {
|
|
12330
|
+
if (this.dialect.supportsReturning()) return;
|
|
12331
|
+
if (tracked.pk != null) return;
|
|
12332
|
+
const pkName = findPrimaryKey(tracked.table);
|
|
12333
|
+
const pkColumn = tracked.table.columns[pkName];
|
|
12334
|
+
if (!pkColumn?.autoIncrement) return;
|
|
12335
|
+
const insertId = results.find((result) => typeof result.insertId === "number")?.insertId;
|
|
12336
|
+
if (insertId == null) return;
|
|
12337
|
+
tracked.entity[pkName] = insertId;
|
|
12338
|
+
}
|
|
11954
12339
|
/**
|
|
11955
12340
|
* Normalizes a column name by removing quotes and table prefixes.
|
|
11956
12341
|
* @param column - The column name to normalize
|
|
@@ -13467,7 +13852,9 @@ function createMysqlExecutor(client) {
|
|
|
13467
13852
|
async executeSql(sql, params) {
|
|
13468
13853
|
const [rows] = await client.query(sql, params);
|
|
13469
13854
|
if (!Array.isArray(rows)) {
|
|
13470
|
-
|
|
13855
|
+
const insertId = rows?.insertId;
|
|
13856
|
+
const normalized = typeof insertId === "number" && insertId > 0 ? insertId : void 0;
|
|
13857
|
+
return [{ columns: [], values: [], insertId: normalized }];
|
|
13471
13858
|
}
|
|
13472
13859
|
const result = rowsToQueryResult(
|
|
13473
13860
|
rows
|
|
@@ -13766,6 +14153,7 @@ function createPooledExecutorFactory(opts) {
|
|
|
13766
14153
|
bitOr,
|
|
13767
14154
|
bitXor,
|
|
13768
14155
|
bootstrapEntities,
|
|
14156
|
+
buildFilterParameters,
|
|
13769
14157
|
caseWhen,
|
|
13770
14158
|
cast,
|
|
13771
14159
|
cbrt,
|
|
@@ -13792,6 +14180,7 @@ function createPooledExecutorFactory(opts) {
|
|
|
13792
14180
|
createExecutorFromQueryRunner,
|
|
13793
14181
|
createMssqlExecutor,
|
|
13794
14182
|
createMysqlExecutor,
|
|
14183
|
+
createParamProxy,
|
|
13795
14184
|
createPooledExecutorFactory,
|
|
13796
14185
|
createPostgresExecutor,
|
|
13797
14186
|
createQueryLoggingExecutor,
|