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.cjs CHANGED
@@ -59,6 +59,7 @@ __export(index_exports, {
59
59
  HasMany: () => HasMany,
60
60
  HasOne: () => HasOne,
61
61
  InsertQueryBuilder: () => InsertQueryBuilder,
62
+ InterceptorPipeline: () => InterceptorPipeline,
62
63
  MySqlDialect: () => MySqlDialect,
63
64
  Orm: () => Orm,
64
65
  OrmSession: () => OrmSession,
@@ -156,6 +157,7 @@ __export(index_exports, {
156
157
  exists: () => exists,
157
158
  exp: () => exp,
158
159
  extract: () => extract,
160
+ extractSchema: () => extractSchema,
159
161
  firstValue: () => firstValue,
160
162
  floor: () => floor,
161
163
  fromUnixTime: () => fromUnixTime,
@@ -166,6 +168,7 @@ __export(index_exports, {
166
168
  getDecoratorMetadata: () => getDecoratorMetadata,
167
169
  getSchemaIntrospector: () => getSchemaIntrospector,
168
170
  getTableDefFromEntity: () => getTableDefFromEntity,
171
+ getTemporalFormat: () => getTemporalFormat,
169
172
  greatest: () => greatest,
170
173
  groupConcat: () => groupConcat,
171
174
  gt: () => gt,
@@ -221,6 +224,8 @@ __export(index_exports, {
221
224
  lt: () => lt,
222
225
  lte: () => lte,
223
226
  ltrim: () => ltrim,
227
+ mapColumnType: () => mapColumnType,
228
+ mapRelationType: () => mapRelationType,
224
229
  materializeAs: () => materializeAs,
225
230
  max: () => max,
226
231
  md5: () => md5,
@@ -266,6 +271,7 @@ __export(index_exports, {
266
271
  rowsToQueryResult: () => rowsToQueryResult,
267
272
  rpad: () => rpad,
268
273
  rtrim: () => rtrim,
274
+ schemaToJson: () => schemaToJson,
269
275
  second: () => second,
270
276
  sel: () => sel,
271
277
  selectFrom: () => selectFrom,
@@ -2545,6 +2551,9 @@ var PostgresDialect = class extends SqlDialectBase {
2545
2551
  quoteIdentifier(id) {
2546
2552
  return `"${id}"`;
2547
2553
  }
2554
+ formatPlaceholder(index) {
2555
+ return `$${index}`;
2556
+ }
2548
2557
  /**
2549
2558
  * Compiles JSON path expression using PostgreSQL syntax
2550
2559
  * @param node - JSON path node
@@ -6595,10 +6604,8 @@ async function executePagedQuery(builder, session, options, countCallback) {
6595
6604
  throw new Error("executePaged: pageSize must be an integer >= 1");
6596
6605
  }
6597
6606
  const offset = (page - 1) * pageSize;
6598
- const [items, totalItems] = await Promise.all([
6599
- builder.limit(pageSize).offset(offset).execute(session),
6600
- countCallback(session)
6601
- ]);
6607
+ const totalItems = await countCallback(session);
6608
+ const items = await builder.limit(pageSize).offset(offset).execute(session);
6602
6609
  return { items, totalItems, page, pageSize };
6603
6610
  }
6604
6611
  function buildWhereHasPredicate(env, context, relationFacet, createChildBuilder, relationName, callbackOrOptions, maybeOptions, negate = false) {
@@ -6942,6 +6949,438 @@ var SelectRelationFacet = class {
6942
6949
  }
6943
6950
  };
6944
6951
 
6952
+ // src/openapi/type-mappers.ts
6953
+ var mapColumnType = (column) => {
6954
+ const sqlType = normalizeType(column.type);
6955
+ const baseSchema = mapSqlTypeToBaseSchema(sqlType, column);
6956
+ const schema = {
6957
+ ...baseSchema,
6958
+ description: column.comment,
6959
+ nullable: !column.notNull && !column.primary
6960
+ };
6961
+ if (column.args && sqlType === "varchar" || sqlType === "char") {
6962
+ schema.maxLength = column.args[0];
6963
+ }
6964
+ if (column.args && sqlType === "decimal" || sqlType === "float") {
6965
+ if (column.args.length >= 1) {
6966
+ schema.minimum = -(10 ** column.args[0]);
6967
+ }
6968
+ }
6969
+ if (sqlType === "enum" && column.args && column.args.length > 0) {
6970
+ schema.enum = column.args;
6971
+ }
6972
+ if (column.default !== void 0) {
6973
+ schema.default = column.default;
6974
+ }
6975
+ return schema;
6976
+ };
6977
+ var normalizeType = (type) => {
6978
+ return type.toLowerCase();
6979
+ };
6980
+ var mapSqlTypeToBaseSchema = (sqlType, column) => {
6981
+ const type = normalizeType(sqlType);
6982
+ const hasCustomTsType = column.tsType !== void 0;
6983
+ switch (type) {
6984
+ case "int":
6985
+ case "integer":
6986
+ case "bigint":
6987
+ return {
6988
+ type: hasCustomTsType ? inferTypeFromTsType(column.tsType) : "integer",
6989
+ format: type === "bigint" ? "int64" : "int32",
6990
+ minimum: column.autoIncrement ? 1 : void 0
6991
+ };
6992
+ case "decimal":
6993
+ case "float":
6994
+ case "double":
6995
+ return {
6996
+ type: hasCustomTsType ? inferTypeFromTsType(column.tsType) : "number"
6997
+ };
6998
+ case "varchar":
6999
+ return {
7000
+ type: "string",
7001
+ minLength: column.notNull ? 1 : void 0,
7002
+ maxLength: column.args?.[0]
7003
+ };
7004
+ case "text":
7005
+ return {
7006
+ type: "string",
7007
+ minLength: column.notNull ? 1 : void 0
7008
+ };
7009
+ case "char":
7010
+ return {
7011
+ type: "string",
7012
+ minLength: column.notNull ? column.args?.[0] || 1 : void 0,
7013
+ maxLength: column.args?.[0]
7014
+ };
7015
+ case "boolean":
7016
+ return {
7017
+ type: "boolean"
7018
+ };
7019
+ case "json":
7020
+ return {
7021
+ anyOf: [
7022
+ { type: "object" },
7023
+ { type: "array" }
7024
+ ]
7025
+ };
7026
+ case "blob":
7027
+ case "binary":
7028
+ case "varbinary":
7029
+ return {
7030
+ type: "string",
7031
+ format: "base64"
7032
+ };
7033
+ case "date":
7034
+ return {
7035
+ type: "string",
7036
+ format: "date"
7037
+ };
7038
+ case "datetime":
7039
+ case "timestamp":
7040
+ return {
7041
+ type: "string",
7042
+ format: "date-time"
7043
+ };
7044
+ case "timestamptz":
7045
+ return {
7046
+ type: "string",
7047
+ format: "date-time"
7048
+ };
7049
+ case "uuid":
7050
+ return {
7051
+ type: "string",
7052
+ format: "uuid",
7053
+ pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
7054
+ };
7055
+ case "enum":
7056
+ return {
7057
+ type: "string",
7058
+ enum: column.args || []
7059
+ };
7060
+ default:
7061
+ if (column.dialectTypes?.postgres && column.dialectTypes.postgres === "bytea") {
7062
+ return {
7063
+ type: "string",
7064
+ format: "base64"
7065
+ };
7066
+ }
7067
+ return {
7068
+ type: "string"
7069
+ };
7070
+ }
7071
+ };
7072
+ var inferTypeFromTsType = (tsType) => {
7073
+ if (typeof tsType === "string") {
7074
+ if (tsType === "number") return "number";
7075
+ if (tsType === "string") return "string";
7076
+ if (tsType === "boolean") return "boolean";
7077
+ }
7078
+ if (typeof tsType === "function") {
7079
+ const typeStr = tsType.name?.toLowerCase();
7080
+ if (typeStr === "number") return "number";
7081
+ if (typeStr === "string") return "string";
7082
+ if (typeStr === "boolean") return "boolean";
7083
+ if (typeStr === "array") return "array";
7084
+ if (typeStr === "object") return "object";
7085
+ }
7086
+ return "string";
7087
+ };
7088
+ var mapRelationType = (relationType) => {
7089
+ switch (relationType) {
7090
+ case "HAS_MANY":
7091
+ case "BELONGS_TO_MANY":
7092
+ return { type: "array", isNullable: false };
7093
+ case "HAS_ONE":
7094
+ case "BELONGS_TO":
7095
+ return { type: "object", isNullable: true };
7096
+ default:
7097
+ return { type: "object", isNullable: true };
7098
+ }
7099
+ };
7100
+ var getTemporalFormat = (sqlType) => {
7101
+ const type = normalizeType(sqlType);
7102
+ switch (type) {
7103
+ case "date":
7104
+ return "date";
7105
+ case "datetime":
7106
+ case "timestamp":
7107
+ case "timestamptz":
7108
+ return "date-time";
7109
+ default:
7110
+ return void 0;
7111
+ }
7112
+ };
7113
+
7114
+ // src/openapi/schema-extractor.ts
7115
+ var extractSchema = (table, plan, projectionNodes, options = {}) => {
7116
+ const mode = options.mode ?? "full";
7117
+ const context = {
7118
+ visitedTables: /* @__PURE__ */ new Set(),
7119
+ schemaCache: /* @__PURE__ */ new Map(),
7120
+ depth: 0,
7121
+ maxDepth: options.maxDepth ?? 5
7122
+ };
7123
+ const hasComputedFields = projectionNodes && projectionNodes.some(
7124
+ (node) => node.type !== "Column"
7125
+ );
7126
+ if (hasComputedFields) {
7127
+ return extractFromProjectionNodes(table, projectionNodes, context, options);
7128
+ }
7129
+ if (mode === "selected" && plan) {
7130
+ return extractSelectedSchema(table, plan, context, options);
7131
+ }
7132
+ return extractFullTableSchema(table, context, options);
7133
+ };
7134
+ var extractFromProjectionNodes = (table, projectionNodes, context, options) => {
7135
+ const properties = {};
7136
+ const required = [];
7137
+ for (const node of projectionNodes) {
7138
+ if (!node || typeof node !== "object") continue;
7139
+ const projection = node;
7140
+ const propertyName = projection.alias ?? "";
7141
+ if (!propertyName) continue;
7142
+ if (projection.type === "Column") {
7143
+ const columnNode4 = node;
7144
+ const column = table.columns[columnNode4.name];
7145
+ if (!column) continue;
7146
+ const property = mapColumnType(column);
7147
+ if (!property.description && options.includeDescriptions && column.comment) {
7148
+ property.description = column.comment;
7149
+ }
7150
+ properties[propertyName] = property;
7151
+ if (column.notNull || column.primary) {
7152
+ required.push(propertyName);
7153
+ }
7154
+ } else if (projection.type === "Function" || projection.type === "WindowFunction") {
7155
+ const fnNode = node;
7156
+ const functionName = fnNode.fn?.toUpperCase() ?? fnNode.name?.toUpperCase() ?? "";
7157
+ const propertySchema = projection.type === "Function" ? mapFunctionNodeToSchema(functionName) : mapWindowFunctionToSchema(functionName);
7158
+ properties[propertyName] = propertySchema;
7159
+ const isCountFunction = functionName === "COUNT";
7160
+ const isWindowRankFunction = functionName === "ROW_NUMBER" || functionName === "RANK";
7161
+ if (isCountFunction || isWindowRankFunction) {
7162
+ required.push(propertyName);
7163
+ }
7164
+ } else if (projection.type === "CaseExpression") {
7165
+ const propertySchema = {
7166
+ type: "string",
7167
+ description: "Computed CASE expression",
7168
+ nullable: true
7169
+ };
7170
+ properties[propertyName] = propertySchema;
7171
+ } else if (projection.type === "ScalarSubquery") {
7172
+ const propertySchema = {
7173
+ type: "object",
7174
+ description: "Subquery result",
7175
+ nullable: true
7176
+ };
7177
+ properties[propertyName] = propertySchema;
7178
+ } else if (projection.type === "CastExpression") {
7179
+ const propertySchema = {
7180
+ type: "string",
7181
+ description: "CAST expression result",
7182
+ nullable: true
7183
+ };
7184
+ properties[propertyName] = propertySchema;
7185
+ }
7186
+ }
7187
+ return {
7188
+ type: "object",
7189
+ properties,
7190
+ required
7191
+ };
7192
+ };
7193
+ var mapFunctionNodeToSchema = (functionName) => {
7194
+ const upperName = functionName.toUpperCase();
7195
+ switch (upperName) {
7196
+ case "COUNT":
7197
+ case "SUM":
7198
+ case "AVG":
7199
+ case "MIN":
7200
+ case "MAX":
7201
+ return {
7202
+ type: "number",
7203
+ description: `${upperName} aggregate function result`,
7204
+ nullable: false
7205
+ };
7206
+ case "GROUP_CONCAT":
7207
+ case "STRING_AGG":
7208
+ case "ARRAY_AGG":
7209
+ return {
7210
+ type: "string",
7211
+ description: `${upperName} aggregate function result`,
7212
+ nullable: true
7213
+ };
7214
+ case "JSON_ARRAYAGG":
7215
+ case "JSON_OBJECTAGG":
7216
+ return {
7217
+ type: "object",
7218
+ description: `${upperName} aggregate function result`,
7219
+ nullable: true
7220
+ };
7221
+ default:
7222
+ return {
7223
+ type: "string",
7224
+ description: `Unknown function: ${functionName}`,
7225
+ nullable: true
7226
+ };
7227
+ }
7228
+ };
7229
+ var mapWindowFunctionToSchema = (functionName) => {
7230
+ const upperName = functionName.toUpperCase();
7231
+ switch (upperName) {
7232
+ case "ROW_NUMBER":
7233
+ case "RANK":
7234
+ case "DENSE_RANK":
7235
+ case "NTILE":
7236
+ return {
7237
+ type: "integer",
7238
+ description: `${upperName} window function result`,
7239
+ nullable: false
7240
+ };
7241
+ case "LAG":
7242
+ case "LEAD":
7243
+ case "FIRST_VALUE":
7244
+ case "LAST_VALUE":
7245
+ return {
7246
+ type: "string",
7247
+ description: `${upperName} window function result`,
7248
+ nullable: true
7249
+ };
7250
+ default:
7251
+ return {
7252
+ type: "string",
7253
+ description: `Unknown window function: ${functionName}`,
7254
+ nullable: true
7255
+ };
7256
+ }
7257
+ };
7258
+ var extractSelectedSchema = (table, plan, context, options) => {
7259
+ const properties = {};
7260
+ const required = [];
7261
+ plan.rootColumns.forEach((columnName) => {
7262
+ const column = table.columns[columnName];
7263
+ if (!column) return;
7264
+ const property = mapColumnType(column);
7265
+ if (!property.description && options.includeDescriptions && column.comment) {
7266
+ property.description = column.comment;
7267
+ }
7268
+ properties[columnName] = property;
7269
+ if (column.notNull || column.primary) {
7270
+ required.push(columnName);
7271
+ }
7272
+ });
7273
+ plan.relations.forEach((relationPlan) => {
7274
+ const relation = table.relations[relationPlan.name];
7275
+ if (!relation) return;
7276
+ const relationSchema = extractRelationSchema(
7277
+ relation,
7278
+ relationPlan,
7279
+ relationPlan.columns,
7280
+ context,
7281
+ options
7282
+ );
7283
+ properties[relationPlan.name] = relationSchema;
7284
+ const { isNullable } = mapRelationType(relation.type);
7285
+ if (!isNullable && relationPlan.name) {
7286
+ required.push(relationPlan.name);
7287
+ }
7288
+ });
7289
+ return {
7290
+ type: "object",
7291
+ properties,
7292
+ required
7293
+ };
7294
+ };
7295
+ var extractFullTableSchema = (table, context, options) => {
7296
+ const cacheKey = table.name;
7297
+ if (context.schemaCache.has(cacheKey)) {
7298
+ return context.schemaCache.get(cacheKey);
7299
+ }
7300
+ 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
+ };
7311
+ }
7312
+ context.visitedTables.add(cacheKey);
7313
+ const properties = {};
7314
+ const required = [];
7315
+ Object.entries(table.columns).forEach(([columnName, column]) => {
7316
+ const property = mapColumnType(column);
7317
+ if (!property.description && options.includeDescriptions && column.comment) {
7318
+ property.description = column.comment;
7319
+ }
7320
+ properties[columnName] = property;
7321
+ if (column.notNull || column.primary) {
7322
+ required.push(columnName);
7323
+ }
7324
+ });
7325
+ Object.entries(table.relations).forEach(([relationName, relation]) => {
7326
+ if (context.depth >= context.maxDepth) {
7327
+ return;
7328
+ }
7329
+ const relationSchema = extractRelationSchema(
7330
+ relation,
7331
+ void 0,
7332
+ [],
7333
+ { ...context, depth: context.depth + 1 },
7334
+ options
7335
+ );
7336
+ properties[relationName] = relationSchema;
7337
+ const { isNullable } = mapRelationType(relation.type);
7338
+ if (!isNullable) {
7339
+ required.push(relationName);
7340
+ }
7341
+ });
7342
+ const schema = {
7343
+ type: "object",
7344
+ properties,
7345
+ required
7346
+ };
7347
+ context.schemaCache.set(cacheKey, schema);
7348
+ return schema;
7349
+ };
7350
+ var extractRelationSchema = (relation, relationPlan, selectedColumns, context, options) => {
7351
+ const targetTable = relation.target;
7352
+ const { type: relationType, isNullable } = mapRelationType(relation.type);
7353
+ let targetSchema;
7354
+ if (relationPlan && selectedColumns.length > 0) {
7355
+ const plan = {
7356
+ rootTable: targetTable.name,
7357
+ rootPrimaryKey: relationPlan.targetPrimaryKey,
7358
+ rootColumns: selectedColumns,
7359
+ relations: []
7360
+ };
7361
+ targetSchema = extractSelectedSchema(targetTable, plan, context, options);
7362
+ } else {
7363
+ targetSchema = extractFullTableSchema(targetTable, context, options);
7364
+ }
7365
+ if (relationType === "array") {
7366
+ return {
7367
+ type: "array",
7368
+ items: targetSchema,
7369
+ nullable: isNullable
7370
+ };
7371
+ }
7372
+ return {
7373
+ type: "object",
7374
+ properties: targetSchema.properties,
7375
+ required: targetSchema.required,
7376
+ nullable: isNullable,
7377
+ description: targetSchema.description
7378
+ };
7379
+ };
7380
+ var schemaToJson = (schema, pretty = false) => {
7381
+ return JSON.stringify(schema, null, pretty ? 2 : 0);
7382
+ };
7383
+
6945
7384
  // src/query-builder/select.ts
6946
7385
  var SelectQueryBuilder = class _SelectQueryBuilder {
6947
7386
  env;
@@ -7486,7 +7925,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
7486
7925
  */
7487
7926
  async executePaged(session, options) {
7488
7927
  const builder = this.ensureDefaultSelection();
7489
- return executePagedQuery(builder, session, options, (sess) => this.count(sess));
7928
+ return executePagedQuery(builder, session, options, (sess) => builder.count(sess));
7490
7929
  }
7491
7930
  /**
7492
7931
  * Executes the query with provided execution and hydration contexts
@@ -7751,7 +8190,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
7751
8190
  return this.compile(dialect).sql;
7752
8191
  }
7753
8192
  /**
7754
- * Gets the hydration plan for the query
8193
+ * Gets hydration plan for query
7755
8194
  * @returns Hydration plan or undefined if none exists
7756
8195
  * @example
7757
8196
  * const plan = qb.include('posts').getHydrationPlan();
@@ -7760,6 +8199,18 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
7760
8199
  getHydrationPlan() {
7761
8200
  return this.context.hydration.getPlan();
7762
8201
  }
8202
+ /**
8203
+ * Gets OpenAPI 3.1 JSON Schema for query result
8204
+ * @param options - Schema generation options
8205
+ * @returns OpenAPI 3.1 JSON Schema for query result
8206
+ * @example
8207
+ * const schema = qb.select('id', 'title', 'author').getSchema();
8208
+ * console.log(JSON.stringify(schema, null, 2));
8209
+ */
8210
+ getSchema(options) {
8211
+ const plan = this.context.hydration.getPlan();
8212
+ return extractSchema(this.env.table, plan, this.context.state.ast.columns, options);
8213
+ }
7763
8214
  /**
7764
8215
  * Gets the Abstract Syntax Tree (AST) representation of the query
7765
8216
  * @returns Query AST with hydration applied
@@ -8789,14 +9240,14 @@ var buildAddColumnSql = (table, colName, dialect) => {
8789
9240
  const rendered = renderColumnDefinition(table, column, dialect);
8790
9241
  return `ALTER TABLE ${dialect.formatTableName(table)} ADD ${rendered.sql};`;
8791
9242
  };
8792
- var normalizeType = (value) => (value || "").toLowerCase().replace(/\s+/g, " ").trim();
9243
+ var normalizeType2 = (value) => (value || "").toLowerCase().replace(/\s+/g, " ").trim();
8793
9244
  var normalizeDefault = (value) => {
8794
9245
  if (value === void 0 || value === null) return void 0;
8795
9246
  return String(value).trim();
8796
9247
  };
8797
9248
  var diffColumn = (expected, actual, dialect) => {
8798
- const expectedType = normalizeType(dialect.renderColumnType(expected));
8799
- const actualType = normalizeType(actual.type);
9249
+ const expectedType = normalizeType2(dialect.renderColumnType(expected));
9250
+ const actualType = normalizeType2(actual.type);
8800
9251
  const expectedDefault = expected.default !== void 0 ? normalizeDefault(dialect.renderDefault(expected.default, expected)) : void 0;
8801
9252
  const actualDefault = normalizeDefault(actual.default);
8802
9253
  return {
@@ -13278,6 +13729,7 @@ function createPooledExecutorFactory(opts) {
13278
13729
  HasMany,
13279
13730
  HasOne,
13280
13731
  InsertQueryBuilder,
13732
+ InterceptorPipeline,
13281
13733
  MySqlDialect,
13282
13734
  Orm,
13283
13735
  OrmSession,
@@ -13375,6 +13827,7 @@ function createPooledExecutorFactory(opts) {
13375
13827
  exists,
13376
13828
  exp,
13377
13829
  extract,
13830
+ extractSchema,
13378
13831
  firstValue,
13379
13832
  floor,
13380
13833
  fromUnixTime,
@@ -13385,6 +13838,7 @@ function createPooledExecutorFactory(opts) {
13385
13838
  getDecoratorMetadata,
13386
13839
  getSchemaIntrospector,
13387
13840
  getTableDefFromEntity,
13841
+ getTemporalFormat,
13388
13842
  greatest,
13389
13843
  groupConcat,
13390
13844
  gt,
@@ -13440,6 +13894,8 @@ function createPooledExecutorFactory(opts) {
13440
13894
  lt,
13441
13895
  lte,
13442
13896
  ltrim,
13897
+ mapColumnType,
13898
+ mapRelationType,
13443
13899
  materializeAs,
13444
13900
  max,
13445
13901
  md5,
@@ -13485,6 +13941,7 @@ function createPooledExecutorFactory(opts) {
13485
13941
  rowsToQueryResult,
13486
13942
  rpad,
13487
13943
  rtrim,
13944
+ schemaToJson,
13488
13945
  second,
13489
13946
  sel,
13490
13947
  selectFrom,