metal-orm 1.0.78 → 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
@@ -6319,10 +6322,8 @@ async function executePagedQuery(builder, session, options, countCallback) {
6319
6322
  throw new Error("executePaged: pageSize must be an integer >= 1");
6320
6323
  }
6321
6324
  const offset = (page - 1) * pageSize;
6322
- const [items, totalItems] = await Promise.all([
6323
- builder.limit(pageSize).offset(offset).execute(session),
6324
- countCallback(session)
6325
- ]);
6325
+ const totalItems = await countCallback(session);
6326
+ const items = await builder.limit(pageSize).offset(offset).execute(session);
6326
6327
  return { items, totalItems, page, pageSize };
6327
6328
  }
6328
6329
  function buildWhereHasPredicate(env, context, relationFacet, createChildBuilder, relationName, callbackOrOptions, maybeOptions, negate = false) {
@@ -6666,6 +6667,438 @@ var SelectRelationFacet = class {
6666
6667
  }
6667
6668
  };
6668
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
+
6669
7102
  // src/query-builder/select.ts
6670
7103
  var SelectQueryBuilder = class _SelectQueryBuilder {
6671
7104
  env;
@@ -7210,7 +7643,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
7210
7643
  */
7211
7644
  async executePaged(session, options) {
7212
7645
  const builder = this.ensureDefaultSelection();
7213
- return executePagedQuery(builder, session, options, (sess) => this.count(sess));
7646
+ return executePagedQuery(builder, session, options, (sess) => builder.count(sess));
7214
7647
  }
7215
7648
  /**
7216
7649
  * Executes the query with provided execution and hydration contexts
@@ -7475,7 +7908,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
7475
7908
  return this.compile(dialect).sql;
7476
7909
  }
7477
7910
  /**
7478
- * Gets the hydration plan for the query
7911
+ * Gets hydration plan for query
7479
7912
  * @returns Hydration plan or undefined if none exists
7480
7913
  * @example
7481
7914
  * const plan = qb.include('posts').getHydrationPlan();
@@ -7484,6 +7917,18 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
7484
7917
  getHydrationPlan() {
7485
7918
  return this.context.hydration.getPlan();
7486
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
+ }
7487
7932
  /**
7488
7933
  * Gets the Abstract Syntax Tree (AST) representation of the query
7489
7934
  * @returns Query AST with hydration applied
@@ -8513,14 +8958,14 @@ var buildAddColumnSql = (table, colName, dialect) => {
8513
8958
  const rendered = renderColumnDefinition(table, column, dialect);
8514
8959
  return `ALTER TABLE ${dialect.formatTableName(table)} ADD ${rendered.sql};`;
8515
8960
  };
8516
- var normalizeType = (value) => (value || "").toLowerCase().replace(/\s+/g, " ").trim();
8961
+ var normalizeType2 = (value) => (value || "").toLowerCase().replace(/\s+/g, " ").trim();
8517
8962
  var normalizeDefault = (value) => {
8518
8963
  if (value === void 0 || value === null) return void 0;
8519
8964
  return String(value).trim();
8520
8965
  };
8521
8966
  var diffColumn = (expected, actual, dialect) => {
8522
- const expectedType = normalizeType(dialect.renderColumnType(expected));
8523
- const actualType = normalizeType(actual.type);
8967
+ const expectedType = normalizeType2(dialect.renderColumnType(expected));
8968
+ const actualType = normalizeType2(actual.type);
8524
8969
  const expectedDefault = expected.default !== void 0 ? normalizeDefault(dialect.renderDefault(expected.default, expected)) : void 0;
8525
8970
  const actualDefault = normalizeDefault(actual.default);
8526
8971
  return {
@@ -13001,6 +13446,7 @@ export {
13001
13446
  HasMany,
13002
13447
  HasOne,
13003
13448
  InsertQueryBuilder,
13449
+ InterceptorPipeline,
13004
13450
  MySqlDialect,
13005
13451
  Orm,
13006
13452
  OrmSession,
@@ -13098,6 +13544,7 @@ export {
13098
13544
  exists,
13099
13545
  exp,
13100
13546
  extract,
13547
+ extractSchema,
13101
13548
  firstValue,
13102
13549
  floor,
13103
13550
  fromUnixTime,
@@ -13108,6 +13555,7 @@ export {
13108
13555
  getDecoratorMetadata,
13109
13556
  getSchemaIntrospector,
13110
13557
  getTableDefFromEntity,
13558
+ getTemporalFormat,
13111
13559
  greatest,
13112
13560
  groupConcat,
13113
13561
  gt,
@@ -13163,6 +13611,8 @@ export {
13163
13611
  lt,
13164
13612
  lte,
13165
13613
  ltrim,
13614
+ mapColumnType,
13615
+ mapRelationType,
13166
13616
  materializeAs,
13167
13617
  max,
13168
13618
  md5,
@@ -13208,6 +13658,7 @@ export {
13208
13658
  rowsToQueryResult,
13209
13659
  rpad,
13210
13660
  rtrim,
13661
+ schemaToJson,
13211
13662
  second,
13212
13663
  sel,
13213
13664
  selectFrom,