metal-orm 1.0.118 → 1.1.0

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
@@ -60,6 +60,7 @@ __export(index_exports, {
60
60
  DateTimeTypeStrategy: () => DateTimeTypeStrategy,
61
61
  DecimalTypeStrategy: () => DecimalTypeStrategy,
62
62
  DefaultBelongsToReference: () => DefaultBelongsToReference,
63
+ DefaultCacheStrategy: () => DefaultCacheStrategy,
63
64
  DefaultEntityMaterializer: () => DefaultEntityMaterializer,
64
65
  DefaultHasManyCollection: () => DefaultHasManyCollection,
65
66
  DefaultManyToManyCollection: () => DefaultManyToManyCollection,
@@ -74,8 +75,10 @@ __export(index_exports, {
74
75
  InsertQueryBuilder: () => InsertQueryBuilder,
75
76
  IntegerTypeStrategy: () => IntegerTypeStrategy,
76
77
  InterceptorPipeline: () => InterceptorPipeline,
78
+ KeyvCacheAdapter: () => KeyvCacheAdapter,
77
79
  Length: () => Length,
78
80
  Lower: () => Lower,
81
+ MemoryCacheAdapter: () => MemoryCacheAdapter,
79
82
  MySqlDialect: () => MySqlDialect,
80
83
  NestedSetStrategy: () => NestedSetStrategy,
81
84
  Orm: () => Orm,
@@ -85,12 +88,14 @@ __export(index_exports, {
85
88
  PostgresDialect: () => PostgresDialect,
86
89
  PrimaryKey: () => PrimaryKey,
87
90
  PrototypeMaterializationStrategy: () => PrototypeMaterializationStrategy,
91
+ QueryCacheManager: () => QueryCacheManager,
88
92
  RelationKinds: () => RelationKinds,
89
93
  STANDARD_COLUMN_TYPES: () => STANDARD_COLUMN_TYPES,
90
94
  SelectQueryBuilder: () => SelectQueryBuilder,
91
95
  SqlServerDialect: () => SqlServerDialect,
92
96
  SqliteDialect: () => SqliteDialect,
93
97
  StringTypeStrategy: () => StringTypeStrategy,
98
+ TagIndex: () => TagIndex,
94
99
  Title: () => Title,
95
100
  Tree: () => Tree,
96
101
  TreeChildren: () => TreeChildren,
@@ -210,6 +215,7 @@ __export(index_exports, {
210
215
  extractScopeValues: () => extractScopeValues,
211
216
  firstValue: () => firstValue,
212
217
  floor: () => floor,
218
+ formatDuration: () => formatDuration,
213
219
  formatTreeList: () => formatTreeList,
214
220
  fromUnixTime: () => fromUnixTime,
215
221
  generateComponentSchemas: () => generateComponentSchemas,
@@ -265,6 +271,7 @@ __export(index_exports, {
265
271
  isOperandNode: () => isOperandNode,
266
272
  isTableDef: () => isTableDef2,
267
273
  isTreeConfig: () => isTreeConfig,
274
+ isValidDuration: () => isValidDuration,
268
275
  isValueOperandInput: () => isValueOperandInput,
269
276
  isWindowFunctionNode: () => isWindowFunctionNode,
270
277
  jsonArrayAgg: () => jsonArrayAgg,
@@ -325,6 +332,7 @@ __export(index_exports, {
325
332
  pagedResponseToOpenApiSchema: () => pagedResponseToOpenApiSchema,
326
333
  paginationParamsSchema: () => paginationParamsSchema,
327
334
  parameterToRef: () => parameterToRef,
335
+ parseDuration: () => parseDuration,
328
336
  pi: () => pi,
329
337
  pick: () => pick,
330
338
  position: () => position,
@@ -1659,7 +1667,7 @@ var Dialect = class _Dialect {
1659
1667
  params: [...ctx.params]
1660
1668
  };
1661
1669
  }
1662
- supportsReturning() {
1670
+ supportsDmlReturningClause() {
1663
1671
  return false;
1664
1672
  }
1665
1673
  /**
@@ -2684,7 +2692,7 @@ var PostgresDialect = class extends SqlDialectBase {
2684
2692
  const columns = this.formatReturningColumns(returning);
2685
2693
  return ` RETURNING ${columns}`;
2686
2694
  }
2687
- supportsReturning() {
2695
+ supportsDmlReturningClause() {
2688
2696
  return true;
2689
2697
  }
2690
2698
  /**
@@ -2999,7 +3007,7 @@ var SqliteDialect = class extends SqlDialectBase {
2999
3007
  return `${this.quoteIdentifier(column.name)}${alias}`;
3000
3008
  }).join(", ");
3001
3009
  }
3002
- supportsReturning() {
3010
+ supportsDmlReturningClause() {
3003
3011
  return true;
3004
3012
  }
3005
3013
  };
@@ -3190,8 +3198,21 @@ var SqlServerDialect = class extends SqlDialectBase {
3190
3198
  this.compileExpression.bind(this)
3191
3199
  );
3192
3200
  const whereClause = this.compileWhere(ast.where, ctx);
3193
- const returning = this.compileReturning(ast.returning, ctx);
3194
- return `DELETE ${this.quoteIdentifier(alias)} FROM ${target}${joins}${whereClause}${returning}`;
3201
+ const returning = this.compileOutputClause(ast.returning, "deleted");
3202
+ return `DELETE ${this.quoteIdentifier(alias)}${returning} FROM ${target}${joins}${whereClause}`;
3203
+ }
3204
+ compileUpdateAst(ast, ctx) {
3205
+ const target = this.compileTableReference(ast.table);
3206
+ const assignments = this.compileUpdateAssignments(ast.set, ast.table, ctx);
3207
+ const output = this.compileReturning(ast.returning, ctx);
3208
+ const fromClause = ast.from ? ` FROM ${this.compileFrom(ast.from, ctx)}` : "";
3209
+ const joins = ast.joins ? ast.joins.map((j) => {
3210
+ const table = this.compileFrom(j.table, ctx);
3211
+ const cond = this.compileExpression(j.condition, ctx);
3212
+ return ` ${j.kind} JOIN ${table} ON ${cond}`;
3213
+ }).join("") : "";
3214
+ const whereClause = this.compileWhere(ast.where, ctx);
3215
+ return `UPDATE ${target} SET ${assignments}${output}${fromClause}${joins}${whereClause}`;
3195
3216
  }
3196
3217
  compileSelectCoreForMssql(ast, ctx) {
3197
3218
  const columns = ast.columns.map((c) => {
@@ -3242,6 +3263,44 @@ var SqlServerDialect = class extends SqlDialectBase {
3242
3263
  }
3243
3264
  return pagination;
3244
3265
  }
3266
+ supportsDmlReturningClause() {
3267
+ return true;
3268
+ }
3269
+ compileReturning(returning, _ctx) {
3270
+ void _ctx;
3271
+ return this.compileOutputClause(returning, "inserted");
3272
+ }
3273
+ compileOutputClause(returning, prefix) {
3274
+ if (!returning || returning.length === 0) return "";
3275
+ const columns = returning.map((column) => {
3276
+ const colName = this.quoteIdentifier(column.name);
3277
+ const alias = column.alias ? ` AS ${this.quoteIdentifier(column.alias)}` : "";
3278
+ return `${prefix}.${colName}${alias}`;
3279
+ }).join(", ");
3280
+ return ` OUTPUT ${columns}`;
3281
+ }
3282
+ compileInsertAst(ast, ctx) {
3283
+ if (!ast.columns.length) {
3284
+ throw new Error("INSERT queries must specify columns.");
3285
+ }
3286
+ const table = this.compileTableName(ast.into);
3287
+ const columnList = ast.columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
3288
+ const output = this.compileReturning(ast.returning, ctx);
3289
+ const source = this.compileInsertValues(ast, ctx);
3290
+ return `INSERT INTO ${table} (${columnList})${output} ${source}`;
3291
+ }
3292
+ compileInsertValues(ast, ctx) {
3293
+ const source = ast.source;
3294
+ if (source.type === "InsertValues") {
3295
+ if (!source.rows.length) {
3296
+ throw new Error("INSERT ... VALUES requires at least one row.");
3297
+ }
3298
+ const values = source.rows.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
3299
+ return `VALUES ${values}`;
3300
+ }
3301
+ const normalized = this.normalizeSelectAst(source.query);
3302
+ return this.compileSelectAst(normalized, ctx).trim();
3303
+ }
3245
3304
  compileCtes(ast, ctx) {
3246
3305
  if (!ast.ctes || ast.ctes.length === 0) return "";
3247
3306
  const defs = ast.ctes.map((cte) => {
@@ -7657,6 +7716,53 @@ var SelectRelationFacet = class {
7657
7716
  }
7658
7717
  };
7659
7718
 
7719
+ // src/query-builder/select/cache-facet.ts
7720
+ var CacheFacet = class {
7721
+ /**
7722
+ * Configura opções de cache no contexto
7723
+ */
7724
+ cache(context, options) {
7725
+ return {
7726
+ state: {
7727
+ ...context.state,
7728
+ options
7729
+ }
7730
+ };
7731
+ }
7732
+ /**
7733
+ * Obtém as opções de cache do contexto
7734
+ */
7735
+ getOptions(context) {
7736
+ return context.state.options;
7737
+ }
7738
+ /**
7739
+ * Verifica se há configuração de cache
7740
+ */
7741
+ hasCache(context) {
7742
+ return context.state.options !== void 0;
7743
+ }
7744
+ /**
7745
+ * Cria opções de cache a partir de parâmetros variados
7746
+ * API flexível para diferentes casos de uso
7747
+ */
7748
+ static createOptions(key, ttl, tagsOrConfig) {
7749
+ let tags;
7750
+ let autoInvalidate;
7751
+ if (Array.isArray(tagsOrConfig)) {
7752
+ tags = tagsOrConfig;
7753
+ } else if (tagsOrConfig) {
7754
+ tags = tagsOrConfig.tags;
7755
+ autoInvalidate = tagsOrConfig.autoInvalidate;
7756
+ }
7757
+ return {
7758
+ key,
7759
+ ttl,
7760
+ tags,
7761
+ autoInvalidate
7762
+ };
7763
+ }
7764
+ };
7765
+
7660
7766
  // src/query-builder/select.ts
7661
7767
  var SelectQueryBuilder = class _SelectQueryBuilder {
7662
7768
  env;
@@ -7673,6 +7779,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
7673
7779
  lazyRelationOptions;
7674
7780
  entityConstructor;
7675
7781
  includeTree;
7782
+ cacheFacet;
7783
+ cacheContext;
7676
7784
  /**
7677
7785
  * Creates a new SelectQueryBuilder instance
7678
7786
  * @param table - Table definition to query
@@ -7680,7 +7788,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
7680
7788
  * @param hydration - Optional hydration manager
7681
7789
  * @param dependencies - Optional query builder dependencies
7682
7790
  */
7683
- constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions, entityConstructor, includeTree) {
7791
+ constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions, entityConstructor, includeTree, cacheContext) {
7684
7792
  const deps = resolveSelectQueryBuilderDependencies(dependencies);
7685
7793
  this.env = { table, deps };
7686
7794
  const createAstService = (nextState) => deps.createQueryAstService(table, nextState);
@@ -7694,6 +7802,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
7694
7802
  this.lazyRelationOptions = new Map(lazyRelationOptions ?? []);
7695
7803
  this.entityConstructor = entityConstructor;
7696
7804
  this.includeTree = includeTree ?? {};
7805
+ this.cacheFacet = new CacheFacet();
7806
+ this.cacheContext = cacheContext ?? { state: {} };
7697
7807
  this.columnSelector = deps.createColumnSelector(this.env);
7698
7808
  const relationManager = deps.createRelationManager(this.env);
7699
7809
  this.fromFacet = new SelectFromFacet(this.env, createAstService);
@@ -7710,7 +7820,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
7710
7820
  * @param lazyRelations - Updated lazy relations set
7711
7821
  * @returns New SelectQueryBuilder instance
7712
7822
  */
7713
- clone(context = this.context, lazyRelations = new Set(this.lazyRelations), lazyRelationOptions = new Map(this.lazyRelationOptions), includeTree = this.includeTree) {
7823
+ clone(context = this.context, lazyRelations = new Set(this.lazyRelations), lazyRelationOptions = new Map(this.lazyRelationOptions), includeTree = this.includeTree, cacheContext = this.cacheContext) {
7714
7824
  return new _SelectQueryBuilder(
7715
7825
  this.env.table,
7716
7826
  context.state,
@@ -7719,7 +7829,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
7719
7829
  lazyRelations,
7720
7830
  lazyRelationOptions,
7721
7831
  this.entityConstructor,
7722
- includeTree
7832
+ includeTree,
7833
+ cacheContext
7723
7834
  );
7724
7835
  }
7725
7836
  /**
@@ -8141,10 +8252,43 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
8141
8252
  }
8142
8253
  return this;
8143
8254
  }
8255
+ /**
8256
+ * Configures caching for this query.
8257
+ * @param key - Unique cache key
8258
+ * @param ttl - Time-to-live (e.g., '1h', '30m', '1d') or milliseconds
8259
+ * @param tagsOrConfig - Optional tags for invalidation or configuration object
8260
+ * @returns New query builder instance with cache configuration
8261
+ * @example
8262
+ * // Simple cache with TTL
8263
+ * await selectFrom(User).cache('active_users', '1h').execute(session);
8264
+ *
8265
+ * // Cache with tags for invalidation
8266
+ * await selectFrom(User)
8267
+ * .cache('users_list', '30m', ['users', 'dashboard'])
8268
+ * .execute(session);
8269
+ *
8270
+ * // Cache with auto-invalidation
8271
+ * await selectFrom(User)
8272
+ * .cache('users_list', '1h', { autoInvalidate: true })
8273
+ * .execute(session);
8274
+ */
8275
+ cache(key, ttl, tagsOrConfig) {
8276
+ const options = CacheFacet.createOptions(key, ttl, tagsOrConfig);
8277
+ const nextCacheContext = this.cacheFacet.cache(this.cacheContext, options);
8278
+ const builder = this.clone(
8279
+ this.context,
8280
+ new Set(this.lazyRelations),
8281
+ new Map(this.lazyRelationOptions),
8282
+ this.includeTree,
8283
+ nextCacheContext
8284
+ );
8285
+ return builder;
8286
+ }
8144
8287
  /**
8145
8288
  * Executes the query and returns hydrated results.
8146
8289
  * If the builder was created with an entity constructor (e.g. via selectFromEntity),
8147
8290
  * this will automatically return fully materialized entity instances.
8291
+ * If caching is configured, results will be cached/retrieved from cache.
8148
8292
  *
8149
8293
  * @param ctx - ORM session context
8150
8294
  * @returns Promise of entity instances (or objects if generic T is not an entity)
@@ -8154,6 +8298,20 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
8154
8298
  * users[0] instanceof User; // true
8155
8299
  */
8156
8300
  async execute(ctx) {
8301
+ const cacheOptions = this.cacheFacet.getOptions(this.cacheContext);
8302
+ if (!cacheOptions || !ctx.cacheManager) {
8303
+ return this.executeWithoutCache(ctx);
8304
+ }
8305
+ return ctx.cacheManager.getOrExecute(
8306
+ cacheOptions,
8307
+ () => this.executeWithoutCache(ctx),
8308
+ ctx.tenantId
8309
+ );
8310
+ }
8311
+ /**
8312
+ * Executa a query sem cache (método interno)
8313
+ */
8314
+ async executeWithoutCache(ctx) {
8157
8315
  if (this.entityConstructor) {
8158
8316
  return this.executeAs(this.entityConstructor, ctx);
8159
8317
  }
@@ -12475,12 +12633,13 @@ var UnitOfWork = class {
12475
12633
  await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
12476
12634
  const payload = this.extractColumns(tracked.table, tracked.entity);
12477
12635
  let builder = new InsertQueryBuilder(tracked.table).values(payload);
12478
- if (this.dialect.supportsReturning()) {
12636
+ if (this.dialect.supportsDmlReturningClause()) {
12479
12637
  builder = builder.returning(...this.getReturningColumns(tracked.table));
12480
12638
  }
12481
12639
  const compiled = builder.compile(this.dialect);
12482
12640
  const results = await this.executeCompiled(compiled);
12483
12641
  this.applyReturningResults(tracked, results);
12642
+ this.applyInsertedIdIfAbsent(tracked, results);
12484
12643
  tracked.status = "managed" /* Managed */;
12485
12644
  tracked.original = this.createSnapshot(tracked.table, tracked.entity);
12486
12645
  tracked.pk = this.getPrimaryKeyValue(tracked);
@@ -12502,7 +12661,7 @@ var UnitOfWork = class {
12502
12661
  const pkColumn = tracked.table.columns[findPrimaryKey(tracked.table)];
12503
12662
  if (!pkColumn) return;
12504
12663
  let builder = new UpdateQueryBuilder(tracked.table).set(changes).where(eq(pkColumn, tracked.pk));
12505
- if (this.dialect.supportsReturning()) {
12664
+ if (this.dialect.supportsDmlReturningClause()) {
12506
12665
  builder = builder.returning(...this.getReturningColumns(tracked.table));
12507
12666
  }
12508
12667
  const compiled = builder.compile(this.dialect);
@@ -12596,9 +12755,8 @@ var UnitOfWork = class {
12596
12755
  * @param results - Query results
12597
12756
  */
12598
12757
  applyReturningResults(tracked, results) {
12599
- if (!this.dialect.supportsReturning()) return;
12600
12758
  const first = results[0];
12601
- if (!first || first.values.length === 0) return;
12759
+ if (!first || first.columns.length === 0 || first.values.length === 0) return;
12602
12760
  const row = first.values[0];
12603
12761
  for (let i = 0; i < first.columns.length; i++) {
12604
12762
  const columnName = this.normalizeColumnName(first.columns[i]);
@@ -12606,6 +12764,21 @@ var UnitOfWork = class {
12606
12764
  tracked.entity[columnName] = row[i];
12607
12765
  }
12608
12766
  }
12767
+ /**
12768
+ * Applies the driver-provided insertId when no RETURNING clause was used.
12769
+ * Only sets the PK if it is currently absent on the entity.
12770
+ * @param tracked - The tracked entity
12771
+ * @param results - Query results (may contain meta.insertId)
12772
+ */
12773
+ applyInsertedIdIfAbsent(tracked, results) {
12774
+ const pkName = findPrimaryKey(tracked.table);
12775
+ const current = tracked.entity[pkName];
12776
+ if (current != null) return;
12777
+ const first = results[0];
12778
+ const insertId = first?.meta?.insertId;
12779
+ if (insertId == null) return;
12780
+ tracked.entity[pkName] = insertId;
12781
+ }
12609
12782
  /**
12610
12783
  * Normalizes a column name by removing quotes and table prefixes.
12611
12784
  * @param column - The column name to normalize
@@ -13243,6 +13416,10 @@ var OrmSession = class {
13243
13416
  domainEvents;
13244
13417
  /** The relation change processor */
13245
13418
  relationChanges;
13419
+ /** The cache manager for query caching */
13420
+ cacheManager;
13421
+ /** The tenant ID for multi-tenancy support */
13422
+ tenantId;
13246
13423
  interceptors;
13247
13424
  saveGraphDefaults;
13248
13425
  /**
@@ -13257,6 +13434,8 @@ var OrmSession = class {
13257
13434
  this.unitOfWork = new UnitOfWork(this.orm.dialect, this.executor, this.identityMap, () => this);
13258
13435
  this.relationChanges = new RelationChangeProcessor(this.unitOfWork, this.orm.dialect, this.executor);
13259
13436
  this.domainEvents = new DomainEventBus(opts.domainEventHandlers);
13437
+ this.cacheManager = opts.cacheManager;
13438
+ this.tenantId = opts.tenantId;
13260
13439
  }
13261
13440
  /**
13262
13441
  * Releases resources associated with this session (executor/pool leases) and resets tracking.
@@ -13631,6 +13810,45 @@ var OrmSession = class {
13631
13810
  entityContext: this
13632
13811
  };
13633
13812
  }
13813
+ /**
13814
+ * Invalidates cache by specific tags.
13815
+ * @param tags - Tags to invalidate
13816
+ * @throws Error if no cache manager is configured
13817
+ * @example
13818
+ * await session.invalidateCacheTags(['users', 'dashboard']);
13819
+ */
13820
+ async invalidateCacheTags(tags) {
13821
+ if (!this.cacheManager) {
13822
+ throw new Error("No cache manager configured. Please provide cacheManager when creating the session.");
13823
+ }
13824
+ await this.cacheManager.invalidateTags(tags);
13825
+ }
13826
+ /**
13827
+ * Invalidates cache by key prefix (useful for multi-tenancy).
13828
+ * @param prefix - Prefix to match cache keys
13829
+ * @throws Error if no cache manager is configured
13830
+ * @example
13831
+ * await session.invalidateCachePrefix('tenant:123:');
13832
+ */
13833
+ async invalidateCachePrefix(prefix) {
13834
+ if (!this.cacheManager) {
13835
+ throw new Error("No cache manager configured. Please provide cacheManager when creating the session.");
13836
+ }
13837
+ await this.cacheManager.invalidatePrefix(prefix);
13838
+ }
13839
+ /**
13840
+ * Invalidates a specific cache key.
13841
+ * @param key - Cache key to invalidate
13842
+ * @throws Error if no cache manager is configured
13843
+ * @example
13844
+ * await session.invalidateCacheKey('active_users');
13845
+ */
13846
+ async invalidateCacheKey(key) {
13847
+ if (!this.cacheManager) {
13848
+ throw new Error("No cache manager configured. Please provide cacheManager when creating the session.");
13849
+ }
13850
+ await this.cacheManager.invalidateKey(key, this.tenantId);
13851
+ }
13634
13852
  /**
13635
13853
  * Merges session defaults with per-call saveGraph options.
13636
13854
  * @param options - Per-call saveGraph options
@@ -13668,6 +13886,309 @@ var InterceptorPipeline = class {
13668
13886
  }
13669
13887
  };
13670
13888
 
13889
+ // src/cache/strategies/default-cache-strategy.ts
13890
+ var DefaultCacheStrategy = class {
13891
+ name = "default";
13892
+ /**
13893
+ * Gera chave de cache com prefixo de tenant se houver
13894
+ */
13895
+ generateKey(queryKey, tenantId) {
13896
+ if (tenantId !== void 0) {
13897
+ return `tenant:${tenantId}:${queryKey}`;
13898
+ }
13899
+ return queryKey;
13900
+ }
13901
+ /**
13902
+ * Verifica se deve cachear baseado na condição configurada
13903
+ */
13904
+ shouldCache(result, options) {
13905
+ if (options.condition) {
13906
+ return options.condition(result);
13907
+ }
13908
+ return true;
13909
+ }
13910
+ /**
13911
+ * Serializa com suporte a tipos especiais
13912
+ */
13913
+ serialize(data) {
13914
+ return JSON.stringify(data, (key, value) => {
13915
+ if (value instanceof Date) {
13916
+ return { __type: "Date", value: value.toISOString() };
13917
+ }
13918
+ if (typeof value === "bigint") {
13919
+ return { __type: "BigInt", value: value.toString() };
13920
+ }
13921
+ if (value instanceof Map) {
13922
+ return { __type: "Map", value: Array.from(value.entries()) };
13923
+ }
13924
+ if (value instanceof Set) {
13925
+ return { __type: "Set", value: Array.from(value) };
13926
+ }
13927
+ return value;
13928
+ });
13929
+ }
13930
+ /**
13931
+ * Desserializa restaurando tipos especiais
13932
+ */
13933
+ deserialize(data) {
13934
+ if (typeof data !== "string") {
13935
+ return data;
13936
+ }
13937
+ return JSON.parse(data, (key, value) => {
13938
+ if (!value || typeof value !== "object") {
13939
+ return value;
13940
+ }
13941
+ if (value.__type === "Date") {
13942
+ return new Date(value.value);
13943
+ }
13944
+ if (value.__type === "BigInt") {
13945
+ return BigInt(value.value);
13946
+ }
13947
+ if (value.__type === "Map") {
13948
+ return new Map(value.value);
13949
+ }
13950
+ if (value.__type === "Set") {
13951
+ return new Set(value.value);
13952
+ }
13953
+ return value;
13954
+ });
13955
+ }
13956
+ };
13957
+
13958
+ // src/cache/duration-utils.ts
13959
+ var DURATION_MULTIPLIERS = {
13960
+ s: 1e3,
13961
+ // segundos
13962
+ m: 6e4,
13963
+ // minutos
13964
+ h: 36e5,
13965
+ // horas
13966
+ d: 864e5,
13967
+ // dias
13968
+ w: 6048e5
13969
+ // semanas
13970
+ };
13971
+ function parseDuration(duration) {
13972
+ if (typeof duration === "number") {
13973
+ return duration;
13974
+ }
13975
+ const match = duration.match(/^(\d+)([smhdw])$/);
13976
+ if (!match) {
13977
+ throw new Error(
13978
+ `Invalid duration format: "${duration}". Use formats like '30s', '10m', '2h', '1d', '1w' or a number in milliseconds.`
13979
+ );
13980
+ }
13981
+ const value = parseInt(match[1], 10);
13982
+ const unit = match[2];
13983
+ return value * DURATION_MULTIPLIERS[unit];
13984
+ }
13985
+ function formatDuration(ms) {
13986
+ if (ms < 1e3) return `${ms}ms`;
13987
+ if (ms < 6e4) return `${Math.floor(ms / 1e3)}s`;
13988
+ if (ms < 36e5) return `${Math.floor(ms / 6e4)}m`;
13989
+ if (ms < 864e5) return `${Math.floor(ms / 36e5)}h`;
13990
+ if (ms < 6048e5) return `${Math.floor(ms / 864e5)}d`;
13991
+ return `${Math.floor(ms / 6048e5)}w`;
13992
+ }
13993
+ function isValidDuration(value) {
13994
+ if (typeof value === "number") {
13995
+ return value >= 0;
13996
+ }
13997
+ if (typeof value === "string") {
13998
+ return /^\d+[smhdw]$/.test(value);
13999
+ }
14000
+ return false;
14001
+ }
14002
+
14003
+ // src/cache/adapters/memory-cache-adapter.ts
14004
+ var MemoryCacheAdapter = class {
14005
+ name = "memory";
14006
+ storage = /* @__PURE__ */ new Map();
14007
+ tagIndex = /* @__PURE__ */ new Map();
14008
+ async get(key) {
14009
+ const entry = this.storage.get(key);
14010
+ if (!entry) {
14011
+ return void 0;
14012
+ }
14013
+ if (entry.expiresAt && Date.now() > entry.expiresAt) {
14014
+ await this.delete(key);
14015
+ return void 0;
14016
+ }
14017
+ return entry.value;
14018
+ }
14019
+ async has(key) {
14020
+ const value = await this.get(key);
14021
+ return value !== void 0;
14022
+ }
14023
+ async set(key, value, ttlMs) {
14024
+ const entry = {
14025
+ value,
14026
+ expiresAt: ttlMs ? Date.now() + ttlMs : void 0
14027
+ };
14028
+ this.storage.set(key, entry);
14029
+ }
14030
+ async delete(key) {
14031
+ this.storage.delete(key);
14032
+ for (const [tag, keys] of this.tagIndex) {
14033
+ keys.delete(key);
14034
+ if (keys.size === 0) {
14035
+ this.tagIndex.delete(tag);
14036
+ }
14037
+ }
14038
+ }
14039
+ async invalidate(key) {
14040
+ await this.delete(key);
14041
+ }
14042
+ async invalidateTags(tags) {
14043
+ const keysToDelete = /* @__PURE__ */ new Set();
14044
+ for (const tag of tags) {
14045
+ const keys = this.tagIndex.get(tag);
14046
+ if (keys) {
14047
+ for (const key of keys) {
14048
+ keysToDelete.add(key);
14049
+ }
14050
+ this.tagIndex.delete(tag);
14051
+ }
14052
+ }
14053
+ for (const key of keysToDelete) {
14054
+ this.storage.delete(key);
14055
+ }
14056
+ }
14057
+ async invalidatePrefix(prefix) {
14058
+ const keysToDelete = [];
14059
+ for (const key of this.storage.keys()) {
14060
+ if (key.startsWith(prefix)) {
14061
+ keysToDelete.push(key);
14062
+ }
14063
+ }
14064
+ for (const key of keysToDelete) {
14065
+ await this.delete(key);
14066
+ }
14067
+ }
14068
+ /**
14069
+ * Registra uma chave com tags (para invalidação)
14070
+ */
14071
+ registerTags(key, tags) {
14072
+ for (const tag of tags) {
14073
+ if (!this.tagIndex.has(tag)) {
14074
+ this.tagIndex.set(tag, /* @__PURE__ */ new Set());
14075
+ }
14076
+ this.tagIndex.get(tag).add(key);
14077
+ }
14078
+ }
14079
+ /**
14080
+ * Limpa todo o cache
14081
+ */
14082
+ clear() {
14083
+ this.storage.clear();
14084
+ this.tagIndex.clear();
14085
+ }
14086
+ /**
14087
+ * Retorna estatísticas do cache
14088
+ */
14089
+ getStats() {
14090
+ return {
14091
+ size: this.storage.size,
14092
+ tags: this.tagIndex.size
14093
+ };
14094
+ }
14095
+ async dispose() {
14096
+ this.clear();
14097
+ }
14098
+ };
14099
+
14100
+ // src/cache/query-cache-manager.ts
14101
+ var QueryCacheManager = class {
14102
+ constructor(provider = new MemoryCacheAdapter(), strategy = new DefaultCacheStrategy(), defaultTtl = "1h") {
14103
+ this.provider = provider;
14104
+ this.strategy = strategy;
14105
+ this.defaultTtl = defaultTtl;
14106
+ }
14107
+ /**
14108
+ * Executa com cache - padrão execute-around
14109
+ * @returns Resultado da execução (do cache ou da função)
14110
+ */
14111
+ async getOrExecute(options, executor, tenantId) {
14112
+ const key = this.strategy.generateKey(options.key, tenantId);
14113
+ const ttlMs = this.parseDuration(options.ttl ?? this.defaultTtl);
14114
+ const cached = await this.provider.get(key);
14115
+ if (cached !== void 0) {
14116
+ return this.strategy.deserialize(cached);
14117
+ }
14118
+ const result = await executor();
14119
+ if (!this.strategy.shouldCache(result, options)) {
14120
+ return result;
14121
+ }
14122
+ const serialized = this.strategy.serialize(result);
14123
+ await this.provider.set(key, serialized, ttlMs);
14124
+ if (options.tags) {
14125
+ await this.registerTags(key, options.tags);
14126
+ }
14127
+ return result;
14128
+ }
14129
+ /**
14130
+ * Invalida uma chave específica
14131
+ */
14132
+ async invalidateKey(key, tenantId) {
14133
+ const fullKey = this.strategy.generateKey(key, tenantId);
14134
+ await this.provider.invalidate(fullKey);
14135
+ }
14136
+ /**
14137
+ * Invalida por tags
14138
+ */
14139
+ async invalidateTags(tags) {
14140
+ await this.provider.invalidateTags(tags);
14141
+ }
14142
+ /**
14143
+ * Invalida por prefixo (útil para multi-tenancy)
14144
+ */
14145
+ async invalidatePrefix(prefix) {
14146
+ await this.provider.invalidatePrefix(prefix);
14147
+ }
14148
+ /**
14149
+ * Limpa todo o cache (cuidado!)
14150
+ */
14151
+ async clear() {
14152
+ const provider = this.provider;
14153
+ if (typeof provider.clear === "function") {
14154
+ provider.clear();
14155
+ } else {
14156
+ throw new Error("Cache provider does not support clear operation");
14157
+ }
14158
+ }
14159
+ /**
14160
+ * Retorna estatísticas do cache (se disponível)
14161
+ */
14162
+ getStats() {
14163
+ const provider = this.provider;
14164
+ if (typeof provider.getStats === "function") {
14165
+ return provider.getStats();
14166
+ }
14167
+ return void 0;
14168
+ }
14169
+ /**
14170
+ * Libera recursos do cache
14171
+ */
14172
+ async dispose() {
14173
+ await this.provider.dispose?.();
14174
+ }
14175
+ /**
14176
+ * Registra tags para uma chave
14177
+ */
14178
+ async registerTags(key, tags) {
14179
+ const provider = this.provider;
14180
+ if (typeof provider.registerTags === "function") {
14181
+ provider.registerTags(key, tags);
14182
+ }
14183
+ }
14184
+ /**
14185
+ * Converte duração para milissegundos
14186
+ */
14187
+ parseDuration(d) {
14188
+ return parseDuration(d);
14189
+ }
14190
+ };
14191
+
13671
14192
  // src/orm/orm.ts
13672
14193
  var Orm = class {
13673
14194
  /** The database dialect */
@@ -13676,6 +14197,8 @@ var Orm = class {
13676
14197
  interceptors;
13677
14198
  /** The naming strategy */
13678
14199
  namingStrategy;
14200
+ /** The cache manager (if configured) */
14201
+ cacheManager;
13679
14202
  executorFactory;
13680
14203
  /**
13681
14204
  * Creates a new ORM instance.
@@ -13686,15 +14209,27 @@ var Orm = class {
13686
14209
  this.interceptors = opts.interceptors ?? new InterceptorPipeline();
13687
14210
  this.namingStrategy = opts.namingStrategy ?? new DefaultNamingStrategy();
13688
14211
  this.executorFactory = opts.executorFactory;
14212
+ if (opts.cache) {
14213
+ this.cacheManager = new QueryCacheManager(
14214
+ opts.cache.provider,
14215
+ opts.cache.strategy ?? new DefaultCacheStrategy(),
14216
+ opts.cache.defaultTtl ?? "1h"
14217
+ );
14218
+ }
13689
14219
  }
13690
14220
  /**
13691
14221
  * Creates a new ORM session.
13692
- * @param options - Optional session options
14222
+ * @param options - Optional session options (e.g., tenantId for multi-tenancy)
13693
14223
  * @returns The ORM session
13694
14224
  */
13695
- createSession() {
14225
+ createSession(options) {
13696
14226
  const executor = this.executorFactory.createExecutor();
13697
- return new OrmSession({ orm: this, executor });
14227
+ return new OrmSession({
14228
+ orm: this,
14229
+ executor,
14230
+ cacheManager: this.cacheManager,
14231
+ tenantId: options?.tenantId
14232
+ });
13698
14233
  }
13699
14234
  /**
13700
14235
  * Executes a function within a transaction.
@@ -13705,7 +14240,11 @@ var Orm = class {
13705
14240
  */
13706
14241
  async transaction(fn8) {
13707
14242
  const executor = this.executorFactory.createTransactionalExecutor();
13708
- const session = new OrmSession({ orm: this, executor });
14243
+ const session = new OrmSession({
14244
+ orm: this,
14245
+ executor,
14246
+ cacheManager: this.cacheManager
14247
+ });
13709
14248
  try {
13710
14249
  return await session.transaction(() => fn8(session));
13711
14250
  } finally {
@@ -14870,7 +15409,15 @@ function createMysqlExecutor(client) {
14870
15409
  async executeSql(sql, params) {
14871
15410
  const [rows] = await client.query(sql, params);
14872
15411
  if (!Array.isArray(rows)) {
14873
- return [{ columns: [], values: [] }];
15412
+ const header = rows;
15413
+ return [{
15414
+ columns: [],
15415
+ values: [],
15416
+ meta: {
15417
+ insertId: header.insertId,
15418
+ rowsAffected: header.affectedRows
15419
+ }
15420
+ }];
14874
15421
  }
14875
15422
  const result = rowsToQueryResult(
14876
15423
  rows
@@ -18179,6 +18726,162 @@ function queryResultsToRows(results) {
18179
18726
  }
18180
18727
  return rows;
18181
18728
  }
18729
+
18730
+ // src/cache/adapters/keyv-cache-adapter.ts
18731
+ var KeyvCacheAdapter = class {
18732
+ constructor(keyv) {
18733
+ this.keyv = keyv;
18734
+ }
18735
+ name = "keyv";
18736
+ async get(key) {
18737
+ return this.keyv.get(key);
18738
+ }
18739
+ async has(key) {
18740
+ const value = await this.keyv.get(key);
18741
+ return value !== void 0;
18742
+ }
18743
+ async set(key, value, ttlMs) {
18744
+ await this.keyv.set(key, value, ttlMs);
18745
+ }
18746
+ async delete(key) {
18747
+ await this.keyv.delete(key);
18748
+ }
18749
+ async invalidate(key) {
18750
+ await this.delete(key);
18751
+ }
18752
+ async invalidateTags(_tags) {
18753
+ throw new Error(
18754
+ "Keyv adapter does not support tag invalidation. Use MemoryCacheAdapter for testing or implement a custom Redis provider."
18755
+ );
18756
+ }
18757
+ async invalidatePrefix(prefix) {
18758
+ if (typeof this.keyv.iterator === "function") {
18759
+ const keys = [];
18760
+ for await (const [key] of this.keyv.iterator()) {
18761
+ if (key.startsWith(prefix)) {
18762
+ keys.push(key);
18763
+ }
18764
+ }
18765
+ if (keys.length > 0) {
18766
+ await Promise.all(keys.map((k) => this.keyv.delete(k)));
18767
+ }
18768
+ return;
18769
+ }
18770
+ throw new Error(
18771
+ "Keyv adapter does not support prefix invalidation in this store. Consider using a store with iterator support."
18772
+ );
18773
+ }
18774
+ async dispose() {
18775
+ await this.keyv.disconnect?.();
18776
+ }
18777
+ };
18778
+
18779
+ // src/cache/tag-index.ts
18780
+ var TagIndex = class {
18781
+ tagToKeys = /* @__PURE__ */ new Map();
18782
+ keyToTags = /* @__PURE__ */ new Map();
18783
+ /**
18784
+ * Registra que uma chave pertence a determinadas tags
18785
+ */
18786
+ register(key, tags) {
18787
+ for (const tag of tags) {
18788
+ if (!this.tagToKeys.has(tag)) {
18789
+ this.tagToKeys.set(tag, /* @__PURE__ */ new Set());
18790
+ }
18791
+ this.tagToKeys.get(tag).add(key);
18792
+ }
18793
+ const existingTags = this.keyToTags.get(key) ?? /* @__PURE__ */ new Set();
18794
+ tags.forEach((tag) => existingTags.add(tag));
18795
+ this.keyToTags.set(key, existingTags);
18796
+ }
18797
+ /**
18798
+ * Remove uma chave do índice
18799
+ */
18800
+ unregister(key) {
18801
+ const tags = this.keyToTags.get(key);
18802
+ if (tags) {
18803
+ for (const tag of tags) {
18804
+ this.tagToKeys.get(tag)?.delete(key);
18805
+ if (this.tagToKeys.get(tag)?.size === 0) {
18806
+ this.tagToKeys.delete(tag);
18807
+ }
18808
+ }
18809
+ this.keyToTags.delete(key);
18810
+ }
18811
+ }
18812
+ /**
18813
+ * Obtém todas as chaves de uma tag
18814
+ */
18815
+ getKeysByTag(tag) {
18816
+ return Array.from(this.tagToKeys.get(tag) ?? []);
18817
+ }
18818
+ /**
18819
+ * Obtém todas as tags de uma chave
18820
+ */
18821
+ getTagsByKey(key) {
18822
+ return Array.from(this.keyToTags.get(key) ?? []);
18823
+ }
18824
+ /**
18825
+ * Invalida todas as chaves de um conjunto de tags
18826
+ * Retorna as chaves afetadas
18827
+ */
18828
+ invalidateTags(tags) {
18829
+ const keysToInvalidate = /* @__PURE__ */ new Set();
18830
+ for (const tag of tags) {
18831
+ const keys = this.tagToKeys.get(tag);
18832
+ if (keys) {
18833
+ for (const key of keys) {
18834
+ keysToInvalidate.add(key);
18835
+ this.unregister(key);
18836
+ }
18837
+ this.tagToKeys.delete(tag);
18838
+ }
18839
+ }
18840
+ return Array.from(keysToInvalidate);
18841
+ }
18842
+ /**
18843
+ * Invalida por prefixo (útil para multi-tenancy)
18844
+ * Retorna as chaves afetadas
18845
+ */
18846
+ invalidatePrefix(prefix) {
18847
+ const keysToInvalidate = [];
18848
+ for (const key of this.keyToTags.keys()) {
18849
+ if (key.startsWith(prefix)) {
18850
+ keysToInvalidate.push(key);
18851
+ this.unregister(key);
18852
+ }
18853
+ }
18854
+ return keysToInvalidate;
18855
+ }
18856
+ /**
18857
+ * Retorna todas as tags registradas
18858
+ */
18859
+ getAllTags() {
18860
+ return Array.from(this.tagToKeys.keys());
18861
+ }
18862
+ /**
18863
+ * Retorna todas as chaves registradas
18864
+ */
18865
+ getAllKeys() {
18866
+ return Array.from(this.keyToTags.keys());
18867
+ }
18868
+ /**
18869
+ * Limpa todo o índice
18870
+ */
18871
+ clear() {
18872
+ this.tagToKeys.clear();
18873
+ this.keyToTags.clear();
18874
+ }
18875
+ /**
18876
+ * Retorna estatísticas do índice
18877
+ */
18878
+ getStats() {
18879
+ return {
18880
+ tags: this.tagToKeys.size,
18881
+ keys: this.keyToTags.size
18882
+ };
18883
+ }
18884
+ };
18182
18885
  // Annotate the CommonJS export names for ESM import in node:
18183
18886
  0 && (module.exports = {
18184
18887
  Alphanumeric,
@@ -18198,6 +18901,7 @@ function queryResultsToRows(results) {
18198
18901
  DateTimeTypeStrategy,
18199
18902
  DecimalTypeStrategy,
18200
18903
  DefaultBelongsToReference,
18904
+ DefaultCacheStrategy,
18201
18905
  DefaultEntityMaterializer,
18202
18906
  DefaultHasManyCollection,
18203
18907
  DefaultManyToManyCollection,
@@ -18212,8 +18916,10 @@ function queryResultsToRows(results) {
18212
18916
  InsertQueryBuilder,
18213
18917
  IntegerTypeStrategy,
18214
18918
  InterceptorPipeline,
18919
+ KeyvCacheAdapter,
18215
18920
  Length,
18216
18921
  Lower,
18922
+ MemoryCacheAdapter,
18217
18923
  MySqlDialect,
18218
18924
  NestedSetStrategy,
18219
18925
  Orm,
@@ -18223,12 +18929,14 @@ function queryResultsToRows(results) {
18223
18929
  PostgresDialect,
18224
18930
  PrimaryKey,
18225
18931
  PrototypeMaterializationStrategy,
18932
+ QueryCacheManager,
18226
18933
  RelationKinds,
18227
18934
  STANDARD_COLUMN_TYPES,
18228
18935
  SelectQueryBuilder,
18229
18936
  SqlServerDialect,
18230
18937
  SqliteDialect,
18231
18938
  StringTypeStrategy,
18939
+ TagIndex,
18232
18940
  Title,
18233
18941
  Tree,
18234
18942
  TreeChildren,
@@ -18348,6 +19056,7 @@ function queryResultsToRows(results) {
18348
19056
  extractScopeValues,
18349
19057
  firstValue,
18350
19058
  floor,
19059
+ formatDuration,
18351
19060
  formatTreeList,
18352
19061
  fromUnixTime,
18353
19062
  generateComponentSchemas,
@@ -18403,6 +19112,7 @@ function queryResultsToRows(results) {
18403
19112
  isOperandNode,
18404
19113
  isTableDef,
18405
19114
  isTreeConfig,
19115
+ isValidDuration,
18406
19116
  isValueOperandInput,
18407
19117
  isWindowFunctionNode,
18408
19118
  jsonArrayAgg,
@@ -18463,6 +19173,7 @@ function queryResultsToRows(results) {
18463
19173
  pagedResponseToOpenApiSchema,
18464
19174
  paginationParamsSchema,
18465
19175
  parameterToRef,
19176
+ parseDuration,
18466
19177
  pi,
18467
19178
  pick,
18468
19179
  position,