metal-orm 1.0.79 → 1.0.80

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.js CHANGED
@@ -2269,6 +2269,9 @@ var PostgresDialect = class extends SqlDialectBase {
2269
2269
  quoteIdentifier(id) {
2270
2270
  return `"${id}"`;
2271
2271
  }
2272
+ formatPlaceholder(index) {
2273
+ return `$${index}`;
2274
+ }
2272
2275
  /**
2273
2276
  * Compiles JSON path expression using PostgreSQL syntax
2274
2277
  * @param node - JSON path node
@@ -6664,6 +6667,438 @@ var SelectRelationFacet = class {
6664
6667
  }
6665
6668
  };
6666
6669
 
6670
+ // src/openapi/type-mappers.ts
6671
+ var mapColumnType = (column) => {
6672
+ const sqlType = normalizeType(column.type);
6673
+ const baseSchema = mapSqlTypeToBaseSchema(sqlType, column);
6674
+ const schema = {
6675
+ ...baseSchema,
6676
+ description: column.comment,
6677
+ nullable: !column.notNull && !column.primary
6678
+ };
6679
+ if (column.args && sqlType === "varchar" || sqlType === "char") {
6680
+ schema.maxLength = column.args[0];
6681
+ }
6682
+ if (column.args && sqlType === "decimal" || sqlType === "float") {
6683
+ if (column.args.length >= 1) {
6684
+ schema.minimum = -(10 ** column.args[0]);
6685
+ }
6686
+ }
6687
+ if (sqlType === "enum" && column.args && column.args.length > 0) {
6688
+ schema.enum = column.args;
6689
+ }
6690
+ if (column.default !== void 0) {
6691
+ schema.default = column.default;
6692
+ }
6693
+ return schema;
6694
+ };
6695
+ var normalizeType = (type) => {
6696
+ return type.toLowerCase();
6697
+ };
6698
+ var mapSqlTypeToBaseSchema = (sqlType, column) => {
6699
+ const type = normalizeType(sqlType);
6700
+ const hasCustomTsType = column.tsType !== void 0;
6701
+ switch (type) {
6702
+ case "int":
6703
+ case "integer":
6704
+ case "bigint":
6705
+ return {
6706
+ type: hasCustomTsType ? inferTypeFromTsType(column.tsType) : "integer",
6707
+ format: type === "bigint" ? "int64" : "int32",
6708
+ minimum: column.autoIncrement ? 1 : void 0
6709
+ };
6710
+ case "decimal":
6711
+ case "float":
6712
+ case "double":
6713
+ return {
6714
+ type: hasCustomTsType ? inferTypeFromTsType(column.tsType) : "number"
6715
+ };
6716
+ case "varchar":
6717
+ return {
6718
+ type: "string",
6719
+ minLength: column.notNull ? 1 : void 0,
6720
+ maxLength: column.args?.[0]
6721
+ };
6722
+ case "text":
6723
+ return {
6724
+ type: "string",
6725
+ minLength: column.notNull ? 1 : void 0
6726
+ };
6727
+ case "char":
6728
+ return {
6729
+ type: "string",
6730
+ minLength: column.notNull ? column.args?.[0] || 1 : void 0,
6731
+ maxLength: column.args?.[0]
6732
+ };
6733
+ case "boolean":
6734
+ return {
6735
+ type: "boolean"
6736
+ };
6737
+ case "json":
6738
+ return {
6739
+ anyOf: [
6740
+ { type: "object" },
6741
+ { type: "array" }
6742
+ ]
6743
+ };
6744
+ case "blob":
6745
+ case "binary":
6746
+ case "varbinary":
6747
+ return {
6748
+ type: "string",
6749
+ format: "base64"
6750
+ };
6751
+ case "date":
6752
+ return {
6753
+ type: "string",
6754
+ format: "date"
6755
+ };
6756
+ case "datetime":
6757
+ case "timestamp":
6758
+ return {
6759
+ type: "string",
6760
+ format: "date-time"
6761
+ };
6762
+ case "timestamptz":
6763
+ return {
6764
+ type: "string",
6765
+ format: "date-time"
6766
+ };
6767
+ case "uuid":
6768
+ return {
6769
+ type: "string",
6770
+ format: "uuid",
6771
+ pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
6772
+ };
6773
+ case "enum":
6774
+ return {
6775
+ type: "string",
6776
+ enum: column.args || []
6777
+ };
6778
+ default:
6779
+ if (column.dialectTypes?.postgres && column.dialectTypes.postgres === "bytea") {
6780
+ return {
6781
+ type: "string",
6782
+ format: "base64"
6783
+ };
6784
+ }
6785
+ return {
6786
+ type: "string"
6787
+ };
6788
+ }
6789
+ };
6790
+ var inferTypeFromTsType = (tsType) => {
6791
+ if (typeof tsType === "string") {
6792
+ if (tsType === "number") return "number";
6793
+ if (tsType === "string") return "string";
6794
+ if (tsType === "boolean") return "boolean";
6795
+ }
6796
+ if (typeof tsType === "function") {
6797
+ const typeStr = tsType.name?.toLowerCase();
6798
+ if (typeStr === "number") return "number";
6799
+ if (typeStr === "string") return "string";
6800
+ if (typeStr === "boolean") return "boolean";
6801
+ if (typeStr === "array") return "array";
6802
+ if (typeStr === "object") return "object";
6803
+ }
6804
+ return "string";
6805
+ };
6806
+ var mapRelationType = (relationType) => {
6807
+ switch (relationType) {
6808
+ case "HAS_MANY":
6809
+ case "BELONGS_TO_MANY":
6810
+ return { type: "array", isNullable: false };
6811
+ case "HAS_ONE":
6812
+ case "BELONGS_TO":
6813
+ return { type: "object", isNullable: true };
6814
+ default:
6815
+ return { type: "object", isNullable: true };
6816
+ }
6817
+ };
6818
+ var getTemporalFormat = (sqlType) => {
6819
+ const type = normalizeType(sqlType);
6820
+ switch (type) {
6821
+ case "date":
6822
+ return "date";
6823
+ case "datetime":
6824
+ case "timestamp":
6825
+ case "timestamptz":
6826
+ return "date-time";
6827
+ default:
6828
+ return void 0;
6829
+ }
6830
+ };
6831
+
6832
+ // src/openapi/schema-extractor.ts
6833
+ var extractSchema = (table, plan, projectionNodes, options = {}) => {
6834
+ const mode = options.mode ?? "full";
6835
+ const context = {
6836
+ visitedTables: /* @__PURE__ */ new Set(),
6837
+ schemaCache: /* @__PURE__ */ new Map(),
6838
+ depth: 0,
6839
+ maxDepth: options.maxDepth ?? 5
6840
+ };
6841
+ const hasComputedFields = projectionNodes && projectionNodes.some(
6842
+ (node) => node.type !== "Column"
6843
+ );
6844
+ if (hasComputedFields) {
6845
+ return extractFromProjectionNodes(table, projectionNodes, context, options);
6846
+ }
6847
+ if (mode === "selected" && plan) {
6848
+ return extractSelectedSchema(table, plan, context, options);
6849
+ }
6850
+ return extractFullTableSchema(table, context, options);
6851
+ };
6852
+ var extractFromProjectionNodes = (table, projectionNodes, context, options) => {
6853
+ const properties = {};
6854
+ const required = [];
6855
+ for (const node of projectionNodes) {
6856
+ if (!node || typeof node !== "object") continue;
6857
+ const projection = node;
6858
+ const propertyName = projection.alias ?? "";
6859
+ if (!propertyName) continue;
6860
+ if (projection.type === "Column") {
6861
+ const columnNode4 = node;
6862
+ const column = table.columns[columnNode4.name];
6863
+ if (!column) continue;
6864
+ const property = mapColumnType(column);
6865
+ if (!property.description && options.includeDescriptions && column.comment) {
6866
+ property.description = column.comment;
6867
+ }
6868
+ properties[propertyName] = property;
6869
+ if (column.notNull || column.primary) {
6870
+ required.push(propertyName);
6871
+ }
6872
+ } else if (projection.type === "Function" || projection.type === "WindowFunction") {
6873
+ const fnNode = node;
6874
+ const functionName = fnNode.fn?.toUpperCase() ?? fnNode.name?.toUpperCase() ?? "";
6875
+ const propertySchema = projection.type === "Function" ? mapFunctionNodeToSchema(functionName) : mapWindowFunctionToSchema(functionName);
6876
+ properties[propertyName] = propertySchema;
6877
+ const isCountFunction = functionName === "COUNT";
6878
+ const isWindowRankFunction = functionName === "ROW_NUMBER" || functionName === "RANK";
6879
+ if (isCountFunction || isWindowRankFunction) {
6880
+ required.push(propertyName);
6881
+ }
6882
+ } else if (projection.type === "CaseExpression") {
6883
+ const propertySchema = {
6884
+ type: "string",
6885
+ description: "Computed CASE expression",
6886
+ nullable: true
6887
+ };
6888
+ properties[propertyName] = propertySchema;
6889
+ } else if (projection.type === "ScalarSubquery") {
6890
+ const propertySchema = {
6891
+ type: "object",
6892
+ description: "Subquery result",
6893
+ nullable: true
6894
+ };
6895
+ properties[propertyName] = propertySchema;
6896
+ } else if (projection.type === "CastExpression") {
6897
+ const propertySchema = {
6898
+ type: "string",
6899
+ description: "CAST expression result",
6900
+ nullable: true
6901
+ };
6902
+ properties[propertyName] = propertySchema;
6903
+ }
6904
+ }
6905
+ return {
6906
+ type: "object",
6907
+ properties,
6908
+ required
6909
+ };
6910
+ };
6911
+ var mapFunctionNodeToSchema = (functionName) => {
6912
+ const upperName = functionName.toUpperCase();
6913
+ switch (upperName) {
6914
+ case "COUNT":
6915
+ case "SUM":
6916
+ case "AVG":
6917
+ case "MIN":
6918
+ case "MAX":
6919
+ return {
6920
+ type: "number",
6921
+ description: `${upperName} aggregate function result`,
6922
+ nullable: false
6923
+ };
6924
+ case "GROUP_CONCAT":
6925
+ case "STRING_AGG":
6926
+ case "ARRAY_AGG":
6927
+ return {
6928
+ type: "string",
6929
+ description: `${upperName} aggregate function result`,
6930
+ nullable: true
6931
+ };
6932
+ case "JSON_ARRAYAGG":
6933
+ case "JSON_OBJECTAGG":
6934
+ return {
6935
+ type: "object",
6936
+ description: `${upperName} aggregate function result`,
6937
+ nullable: true
6938
+ };
6939
+ default:
6940
+ return {
6941
+ type: "string",
6942
+ description: `Unknown function: ${functionName}`,
6943
+ nullable: true
6944
+ };
6945
+ }
6946
+ };
6947
+ var mapWindowFunctionToSchema = (functionName) => {
6948
+ const upperName = functionName.toUpperCase();
6949
+ switch (upperName) {
6950
+ case "ROW_NUMBER":
6951
+ case "RANK":
6952
+ case "DENSE_RANK":
6953
+ case "NTILE":
6954
+ return {
6955
+ type: "integer",
6956
+ description: `${upperName} window function result`,
6957
+ nullable: false
6958
+ };
6959
+ case "LAG":
6960
+ case "LEAD":
6961
+ case "FIRST_VALUE":
6962
+ case "LAST_VALUE":
6963
+ return {
6964
+ type: "string",
6965
+ description: `${upperName} window function result`,
6966
+ nullable: true
6967
+ };
6968
+ default:
6969
+ return {
6970
+ type: "string",
6971
+ description: `Unknown window function: ${functionName}`,
6972
+ nullable: true
6973
+ };
6974
+ }
6975
+ };
6976
+ var extractSelectedSchema = (table, plan, context, options) => {
6977
+ const properties = {};
6978
+ const required = [];
6979
+ plan.rootColumns.forEach((columnName) => {
6980
+ const column = table.columns[columnName];
6981
+ if (!column) return;
6982
+ const property = mapColumnType(column);
6983
+ if (!property.description && options.includeDescriptions && column.comment) {
6984
+ property.description = column.comment;
6985
+ }
6986
+ properties[columnName] = property;
6987
+ if (column.notNull || column.primary) {
6988
+ required.push(columnName);
6989
+ }
6990
+ });
6991
+ plan.relations.forEach((relationPlan) => {
6992
+ const relation = table.relations[relationPlan.name];
6993
+ if (!relation) return;
6994
+ const relationSchema = extractRelationSchema(
6995
+ relation,
6996
+ relationPlan,
6997
+ relationPlan.columns,
6998
+ context,
6999
+ options
7000
+ );
7001
+ properties[relationPlan.name] = relationSchema;
7002
+ const { isNullable } = mapRelationType(relation.type);
7003
+ if (!isNullable && relationPlan.name) {
7004
+ required.push(relationPlan.name);
7005
+ }
7006
+ });
7007
+ return {
7008
+ type: "object",
7009
+ properties,
7010
+ required
7011
+ };
7012
+ };
7013
+ var extractFullTableSchema = (table, context, options) => {
7014
+ const cacheKey = table.name;
7015
+ if (context.schemaCache.has(cacheKey)) {
7016
+ return context.schemaCache.get(cacheKey);
7017
+ }
7018
+ if (context.visitedTables.has(cacheKey) && context.depth > 0) {
7019
+ return {
7020
+ type: "object",
7021
+ properties: {
7022
+ _ref: {
7023
+ type: "string",
7024
+ description: `Circular reference to ${table.name}`
7025
+ }
7026
+ },
7027
+ required: []
7028
+ };
7029
+ }
7030
+ context.visitedTables.add(cacheKey);
7031
+ const properties = {};
7032
+ const required = [];
7033
+ Object.entries(table.columns).forEach(([columnName, column]) => {
7034
+ const property = mapColumnType(column);
7035
+ if (!property.description && options.includeDescriptions && column.comment) {
7036
+ property.description = column.comment;
7037
+ }
7038
+ properties[columnName] = property;
7039
+ if (column.notNull || column.primary) {
7040
+ required.push(columnName);
7041
+ }
7042
+ });
7043
+ Object.entries(table.relations).forEach(([relationName, relation]) => {
7044
+ if (context.depth >= context.maxDepth) {
7045
+ return;
7046
+ }
7047
+ const relationSchema = extractRelationSchema(
7048
+ relation,
7049
+ void 0,
7050
+ [],
7051
+ { ...context, depth: context.depth + 1 },
7052
+ options
7053
+ );
7054
+ properties[relationName] = relationSchema;
7055
+ const { isNullable } = mapRelationType(relation.type);
7056
+ if (!isNullable) {
7057
+ required.push(relationName);
7058
+ }
7059
+ });
7060
+ const schema = {
7061
+ type: "object",
7062
+ properties,
7063
+ required
7064
+ };
7065
+ context.schemaCache.set(cacheKey, schema);
7066
+ return schema;
7067
+ };
7068
+ var extractRelationSchema = (relation, relationPlan, selectedColumns, context, options) => {
7069
+ const targetTable = relation.target;
7070
+ const { type: relationType, isNullable } = mapRelationType(relation.type);
7071
+ let targetSchema;
7072
+ if (relationPlan && selectedColumns.length > 0) {
7073
+ const plan = {
7074
+ rootTable: targetTable.name,
7075
+ rootPrimaryKey: relationPlan.targetPrimaryKey,
7076
+ rootColumns: selectedColumns,
7077
+ relations: []
7078
+ };
7079
+ targetSchema = extractSelectedSchema(targetTable, plan, context, options);
7080
+ } else {
7081
+ targetSchema = extractFullTableSchema(targetTable, context, options);
7082
+ }
7083
+ if (relationType === "array") {
7084
+ return {
7085
+ type: "array",
7086
+ items: targetSchema,
7087
+ nullable: isNullable
7088
+ };
7089
+ }
7090
+ return {
7091
+ type: "object",
7092
+ properties: targetSchema.properties,
7093
+ required: targetSchema.required,
7094
+ nullable: isNullable,
7095
+ description: targetSchema.description
7096
+ };
7097
+ };
7098
+ var schemaToJson = (schema, pretty = false) => {
7099
+ return JSON.stringify(schema, null, pretty ? 2 : 0);
7100
+ };
7101
+
6667
7102
  // src/query-builder/select.ts
6668
7103
  var SelectQueryBuilder = class _SelectQueryBuilder {
6669
7104
  env;
@@ -7473,7 +7908,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
7473
7908
  return this.compile(dialect).sql;
7474
7909
  }
7475
7910
  /**
7476
- * Gets the hydration plan for the query
7911
+ * Gets hydration plan for query
7477
7912
  * @returns Hydration plan or undefined if none exists
7478
7913
  * @example
7479
7914
  * const plan = qb.include('posts').getHydrationPlan();
@@ -7482,6 +7917,18 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
7482
7917
  getHydrationPlan() {
7483
7918
  return this.context.hydration.getPlan();
7484
7919
  }
7920
+ /**
7921
+ * Gets OpenAPI 3.1 JSON Schema for query result
7922
+ * @param options - Schema generation options
7923
+ * @returns OpenAPI 3.1 JSON Schema for query result
7924
+ * @example
7925
+ * const schema = qb.select('id', 'title', 'author').getSchema();
7926
+ * console.log(JSON.stringify(schema, null, 2));
7927
+ */
7928
+ getSchema(options) {
7929
+ const plan = this.context.hydration.getPlan();
7930
+ return extractSchema(this.env.table, plan, this.context.state.ast.columns, options);
7931
+ }
7485
7932
  /**
7486
7933
  * Gets the Abstract Syntax Tree (AST) representation of the query
7487
7934
  * @returns Query AST with hydration applied
@@ -8511,14 +8958,14 @@ var buildAddColumnSql = (table, colName, dialect) => {
8511
8958
  const rendered = renderColumnDefinition(table, column, dialect);
8512
8959
  return `ALTER TABLE ${dialect.formatTableName(table)} ADD ${rendered.sql};`;
8513
8960
  };
8514
- var normalizeType = (value) => (value || "").toLowerCase().replace(/\s+/g, " ").trim();
8961
+ var normalizeType2 = (value) => (value || "").toLowerCase().replace(/\s+/g, " ").trim();
8515
8962
  var normalizeDefault = (value) => {
8516
8963
  if (value === void 0 || value === null) return void 0;
8517
8964
  return String(value).trim();
8518
8965
  };
8519
8966
  var diffColumn = (expected, actual, dialect) => {
8520
- const expectedType = normalizeType(dialect.renderColumnType(expected));
8521
- const actualType = normalizeType(actual.type);
8967
+ const expectedType = normalizeType2(dialect.renderColumnType(expected));
8968
+ const actualType = normalizeType2(actual.type);
8522
8969
  const expectedDefault = expected.default !== void 0 ? normalizeDefault(dialect.renderDefault(expected.default, expected)) : void 0;
8523
8970
  const actualDefault = normalizeDefault(actual.default);
8524
8971
  return {
@@ -12999,6 +13446,7 @@ export {
12999
13446
  HasMany,
13000
13447
  HasOne,
13001
13448
  InsertQueryBuilder,
13449
+ InterceptorPipeline,
13002
13450
  MySqlDialect,
13003
13451
  Orm,
13004
13452
  OrmSession,
@@ -13096,6 +13544,7 @@ export {
13096
13544
  exists,
13097
13545
  exp,
13098
13546
  extract,
13547
+ extractSchema,
13099
13548
  firstValue,
13100
13549
  floor,
13101
13550
  fromUnixTime,
@@ -13106,6 +13555,7 @@ export {
13106
13555
  getDecoratorMetadata,
13107
13556
  getSchemaIntrospector,
13108
13557
  getTableDefFromEntity,
13558
+ getTemporalFormat,
13109
13559
  greatest,
13110
13560
  groupConcat,
13111
13561
  gt,
@@ -13161,6 +13611,8 @@ export {
13161
13611
  lt,
13162
13612
  lte,
13163
13613
  ltrim,
13614
+ mapColumnType,
13615
+ mapRelationType,
13164
13616
  materializeAs,
13165
13617
  max,
13166
13618
  md5,
@@ -13206,6 +13658,7 @@ export {
13206
13658
  rowsToQueryResult,
13207
13659
  rpad,
13208
13660
  rtrim,
13661
+ schemaToJson,
13209
13662
  second,
13210
13663
  sel,
13211
13664
  selectFrom,