metal-orm 1.0.117 → 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/README.md +26 -14
- package/dist/index.cjs +751 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +419 -7
- package/dist/index.d.ts +419 -7
- package/dist/index.js +743 -18
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
- package/src/cache/adapters/index.ts +2 -0
- package/src/cache/adapters/keyv-cache-adapter.ts +81 -0
- package/src/cache/adapters/memory-cache-adapter.ts +127 -0
- package/src/cache/cache-interfaces.ts +70 -0
- package/src/cache/duration-utils.ts +82 -0
- package/src/cache/index.ts +28 -0
- package/src/cache/query-cache-manager.ts +130 -0
- package/src/cache/strategies/cache-strategy.ts +29 -0
- package/src/cache/strategies/default-cache-strategy.ts +96 -0
- package/src/cache/strategies/index.ts +2 -0
- package/src/cache/tag-index.ts +128 -0
- package/src/core/dialect/abstract.ts +565 -565
- package/src/core/dialect/mssql/index.ts +68 -3
- package/src/core/dialect/postgres/index.ts +1 -1
- package/src/core/dialect/sqlite/index.ts +1 -1
- package/src/core/execution/db-executor.ts +107 -103
- package/src/core/execution/executors/mysql-executor.ts +9 -2
- package/src/index.ts +3 -0
- package/src/orm/orm-session.ts +616 -563
- package/src/orm/orm.ts +108 -71
- package/src/orm/relation-preload.ts +38 -1
- package/src/orm/unit-of-work.ts +22 -4
- package/src/query-builder/select/cache-facet.ts +67 -0
- package/src/query-builder/select.ts +125 -57
package/dist/index.js
CHANGED
|
@@ -1268,7 +1268,7 @@ var Dialect = class _Dialect {
|
|
|
1268
1268
|
params: [...ctx.params]
|
|
1269
1269
|
};
|
|
1270
1270
|
}
|
|
1271
|
-
|
|
1271
|
+
supportsDmlReturningClause() {
|
|
1272
1272
|
return false;
|
|
1273
1273
|
}
|
|
1274
1274
|
/**
|
|
@@ -2293,7 +2293,7 @@ var PostgresDialect = class extends SqlDialectBase {
|
|
|
2293
2293
|
const columns = this.formatReturningColumns(returning);
|
|
2294
2294
|
return ` RETURNING ${columns}`;
|
|
2295
2295
|
}
|
|
2296
|
-
|
|
2296
|
+
supportsDmlReturningClause() {
|
|
2297
2297
|
return true;
|
|
2298
2298
|
}
|
|
2299
2299
|
/**
|
|
@@ -2608,7 +2608,7 @@ var SqliteDialect = class extends SqlDialectBase {
|
|
|
2608
2608
|
return `${this.quoteIdentifier(column.name)}${alias}`;
|
|
2609
2609
|
}).join(", ");
|
|
2610
2610
|
}
|
|
2611
|
-
|
|
2611
|
+
supportsDmlReturningClause() {
|
|
2612
2612
|
return true;
|
|
2613
2613
|
}
|
|
2614
2614
|
};
|
|
@@ -2799,8 +2799,21 @@ var SqlServerDialect = class extends SqlDialectBase {
|
|
|
2799
2799
|
this.compileExpression.bind(this)
|
|
2800
2800
|
);
|
|
2801
2801
|
const whereClause = this.compileWhere(ast.where, ctx);
|
|
2802
|
-
const returning = this.
|
|
2803
|
-
return `DELETE ${this.quoteIdentifier(alias)} FROM ${target}${joins}${whereClause}
|
|
2802
|
+
const returning = this.compileOutputClause(ast.returning, "deleted");
|
|
2803
|
+
return `DELETE ${this.quoteIdentifier(alias)}${returning} FROM ${target}${joins}${whereClause}`;
|
|
2804
|
+
}
|
|
2805
|
+
compileUpdateAst(ast, ctx) {
|
|
2806
|
+
const target = this.compileTableReference(ast.table);
|
|
2807
|
+
const assignments = this.compileUpdateAssignments(ast.set, ast.table, ctx);
|
|
2808
|
+
const output = this.compileReturning(ast.returning, ctx);
|
|
2809
|
+
const fromClause = ast.from ? ` FROM ${this.compileFrom(ast.from, ctx)}` : "";
|
|
2810
|
+
const joins = ast.joins ? ast.joins.map((j) => {
|
|
2811
|
+
const table = this.compileFrom(j.table, ctx);
|
|
2812
|
+
const cond = this.compileExpression(j.condition, ctx);
|
|
2813
|
+
return ` ${j.kind} JOIN ${table} ON ${cond}`;
|
|
2814
|
+
}).join("") : "";
|
|
2815
|
+
const whereClause = this.compileWhere(ast.where, ctx);
|
|
2816
|
+
return `UPDATE ${target} SET ${assignments}${output}${fromClause}${joins}${whereClause}`;
|
|
2804
2817
|
}
|
|
2805
2818
|
compileSelectCoreForMssql(ast, ctx) {
|
|
2806
2819
|
const columns = ast.columns.map((c) => {
|
|
@@ -2851,6 +2864,44 @@ var SqlServerDialect = class extends SqlDialectBase {
|
|
|
2851
2864
|
}
|
|
2852
2865
|
return pagination;
|
|
2853
2866
|
}
|
|
2867
|
+
supportsDmlReturningClause() {
|
|
2868
|
+
return true;
|
|
2869
|
+
}
|
|
2870
|
+
compileReturning(returning, _ctx) {
|
|
2871
|
+
void _ctx;
|
|
2872
|
+
return this.compileOutputClause(returning, "inserted");
|
|
2873
|
+
}
|
|
2874
|
+
compileOutputClause(returning, prefix) {
|
|
2875
|
+
if (!returning || returning.length === 0) return "";
|
|
2876
|
+
const columns = returning.map((column) => {
|
|
2877
|
+
const colName = this.quoteIdentifier(column.name);
|
|
2878
|
+
const alias = column.alias ? ` AS ${this.quoteIdentifier(column.alias)}` : "";
|
|
2879
|
+
return `${prefix}.${colName}${alias}`;
|
|
2880
|
+
}).join(", ");
|
|
2881
|
+
return ` OUTPUT ${columns}`;
|
|
2882
|
+
}
|
|
2883
|
+
compileInsertAst(ast, ctx) {
|
|
2884
|
+
if (!ast.columns.length) {
|
|
2885
|
+
throw new Error("INSERT queries must specify columns.");
|
|
2886
|
+
}
|
|
2887
|
+
const table = this.compileTableName(ast.into);
|
|
2888
|
+
const columnList = ast.columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
|
|
2889
|
+
const output = this.compileReturning(ast.returning, ctx);
|
|
2890
|
+
const source = this.compileInsertValues(ast, ctx);
|
|
2891
|
+
return `INSERT INTO ${table} (${columnList})${output} ${source}`;
|
|
2892
|
+
}
|
|
2893
|
+
compileInsertValues(ast, ctx) {
|
|
2894
|
+
const source = ast.source;
|
|
2895
|
+
if (source.type === "InsertValues") {
|
|
2896
|
+
if (!source.rows.length) {
|
|
2897
|
+
throw new Error("INSERT ... VALUES requires at least one row.");
|
|
2898
|
+
}
|
|
2899
|
+
const values = source.rows.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
|
|
2900
|
+
return `VALUES ${values}`;
|
|
2901
|
+
}
|
|
2902
|
+
const normalized = this.normalizeSelectAst(source.query);
|
|
2903
|
+
return this.compileSelectAst(normalized, ctx).trim();
|
|
2904
|
+
}
|
|
2854
2905
|
compileCtes(ast, ctx) {
|
|
2855
2906
|
if (!ast.ctes || ast.ctes.length === 0) return "";
|
|
2856
2907
|
const defs = ast.ctes.map((cte) => {
|
|
@@ -6524,10 +6575,28 @@ var setLazyOptionsIfEmpty = (entity, relationName, options) => {
|
|
|
6524
6575
|
if (!meta || meta.lazyRelationOptions.has(relationName)) return;
|
|
6525
6576
|
meta.lazyRelationOptions.set(relationName, options);
|
|
6526
6577
|
};
|
|
6578
|
+
var batchByTable = (pending) => {
|
|
6579
|
+
if (pending.length <= 1) return pending;
|
|
6580
|
+
const byTable = /* @__PURE__ */ new Map();
|
|
6581
|
+
let ungroupedIndex = 0;
|
|
6582
|
+
for (const { entities, include } of pending) {
|
|
6583
|
+
const meta = entities.length ? getEntityMeta(entities[0]) : void 0;
|
|
6584
|
+
const tableKey2 = meta?.table?.name ?? `__ungrouped_${ungroupedIndex++}`;
|
|
6585
|
+
const existing = byTable.get(tableKey2);
|
|
6586
|
+
if (existing) {
|
|
6587
|
+
existing.entities.push(...entities);
|
|
6588
|
+
existing.include = mergeRelationIncludeTrees(existing.include, include);
|
|
6589
|
+
} else {
|
|
6590
|
+
byTable.set(tableKey2, { entities: [...entities], include: { ...include } });
|
|
6591
|
+
}
|
|
6592
|
+
}
|
|
6593
|
+
return Array.from(byTable.values());
|
|
6594
|
+
};
|
|
6527
6595
|
var preloadRelationIncludes = async (entities, includeTree, depth = 0) => {
|
|
6528
6596
|
if (!entities.length) return;
|
|
6529
6597
|
const entries = Object.entries(includeTree);
|
|
6530
6598
|
if (!entries.length) return;
|
|
6599
|
+
const pending = [];
|
|
6531
6600
|
for (const [relationName, node] of entries) {
|
|
6532
6601
|
const shouldLoad = depth > 0 || Boolean(node.include);
|
|
6533
6602
|
if (!shouldLoad) continue;
|
|
@@ -6539,9 +6608,13 @@ var preloadRelationIncludes = async (entities, includeTree, depth = 0) => {
|
|
|
6539
6608
|
);
|
|
6540
6609
|
const relatedEntities = loaded.flat();
|
|
6541
6610
|
if (node.include && relatedEntities.length) {
|
|
6542
|
-
|
|
6611
|
+
pending.push({ entities: relatedEntities, include: node.include });
|
|
6543
6612
|
}
|
|
6544
6613
|
}
|
|
6614
|
+
const batched = batchByTable(pending);
|
|
6615
|
+
for (const { entities: batchEntities, include } of batched) {
|
|
6616
|
+
await preloadRelationIncludes(batchEntities, include, depth + 1);
|
|
6617
|
+
}
|
|
6545
6618
|
};
|
|
6546
6619
|
|
|
6547
6620
|
// src/orm/execute.ts
|
|
@@ -7244,6 +7317,53 @@ var SelectRelationFacet = class {
|
|
|
7244
7317
|
}
|
|
7245
7318
|
};
|
|
7246
7319
|
|
|
7320
|
+
// src/query-builder/select/cache-facet.ts
|
|
7321
|
+
var CacheFacet = class {
|
|
7322
|
+
/**
|
|
7323
|
+
* Configura opções de cache no contexto
|
|
7324
|
+
*/
|
|
7325
|
+
cache(context, options) {
|
|
7326
|
+
return {
|
|
7327
|
+
state: {
|
|
7328
|
+
...context.state,
|
|
7329
|
+
options
|
|
7330
|
+
}
|
|
7331
|
+
};
|
|
7332
|
+
}
|
|
7333
|
+
/**
|
|
7334
|
+
* Obtém as opções de cache do contexto
|
|
7335
|
+
*/
|
|
7336
|
+
getOptions(context) {
|
|
7337
|
+
return context.state.options;
|
|
7338
|
+
}
|
|
7339
|
+
/**
|
|
7340
|
+
* Verifica se há configuração de cache
|
|
7341
|
+
*/
|
|
7342
|
+
hasCache(context) {
|
|
7343
|
+
return context.state.options !== void 0;
|
|
7344
|
+
}
|
|
7345
|
+
/**
|
|
7346
|
+
* Cria opções de cache a partir de parâmetros variados
|
|
7347
|
+
* API flexível para diferentes casos de uso
|
|
7348
|
+
*/
|
|
7349
|
+
static createOptions(key, ttl, tagsOrConfig) {
|
|
7350
|
+
let tags;
|
|
7351
|
+
let autoInvalidate;
|
|
7352
|
+
if (Array.isArray(tagsOrConfig)) {
|
|
7353
|
+
tags = tagsOrConfig;
|
|
7354
|
+
} else if (tagsOrConfig) {
|
|
7355
|
+
tags = tagsOrConfig.tags;
|
|
7356
|
+
autoInvalidate = tagsOrConfig.autoInvalidate;
|
|
7357
|
+
}
|
|
7358
|
+
return {
|
|
7359
|
+
key,
|
|
7360
|
+
ttl,
|
|
7361
|
+
tags,
|
|
7362
|
+
autoInvalidate
|
|
7363
|
+
};
|
|
7364
|
+
}
|
|
7365
|
+
};
|
|
7366
|
+
|
|
7247
7367
|
// src/query-builder/select.ts
|
|
7248
7368
|
var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
7249
7369
|
env;
|
|
@@ -7260,6 +7380,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7260
7380
|
lazyRelationOptions;
|
|
7261
7381
|
entityConstructor;
|
|
7262
7382
|
includeTree;
|
|
7383
|
+
cacheFacet;
|
|
7384
|
+
cacheContext;
|
|
7263
7385
|
/**
|
|
7264
7386
|
* Creates a new SelectQueryBuilder instance
|
|
7265
7387
|
* @param table - Table definition to query
|
|
@@ -7267,7 +7389,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7267
7389
|
* @param hydration - Optional hydration manager
|
|
7268
7390
|
* @param dependencies - Optional query builder dependencies
|
|
7269
7391
|
*/
|
|
7270
|
-
constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions, entityConstructor, includeTree) {
|
|
7392
|
+
constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions, entityConstructor, includeTree, cacheContext) {
|
|
7271
7393
|
const deps = resolveSelectQueryBuilderDependencies(dependencies);
|
|
7272
7394
|
this.env = { table, deps };
|
|
7273
7395
|
const createAstService = (nextState) => deps.createQueryAstService(table, nextState);
|
|
@@ -7281,6 +7403,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7281
7403
|
this.lazyRelationOptions = new Map(lazyRelationOptions ?? []);
|
|
7282
7404
|
this.entityConstructor = entityConstructor;
|
|
7283
7405
|
this.includeTree = includeTree ?? {};
|
|
7406
|
+
this.cacheFacet = new CacheFacet();
|
|
7407
|
+
this.cacheContext = cacheContext ?? { state: {} };
|
|
7284
7408
|
this.columnSelector = deps.createColumnSelector(this.env);
|
|
7285
7409
|
const relationManager = deps.createRelationManager(this.env);
|
|
7286
7410
|
this.fromFacet = new SelectFromFacet(this.env, createAstService);
|
|
@@ -7297,7 +7421,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7297
7421
|
* @param lazyRelations - Updated lazy relations set
|
|
7298
7422
|
* @returns New SelectQueryBuilder instance
|
|
7299
7423
|
*/
|
|
7300
|
-
clone(context = this.context, lazyRelations = new Set(this.lazyRelations), lazyRelationOptions = new Map(this.lazyRelationOptions), includeTree = this.includeTree) {
|
|
7424
|
+
clone(context = this.context, lazyRelations = new Set(this.lazyRelations), lazyRelationOptions = new Map(this.lazyRelationOptions), includeTree = this.includeTree, cacheContext = this.cacheContext) {
|
|
7301
7425
|
return new _SelectQueryBuilder(
|
|
7302
7426
|
this.env.table,
|
|
7303
7427
|
context.state,
|
|
@@ -7306,7 +7430,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7306
7430
|
lazyRelations,
|
|
7307
7431
|
lazyRelationOptions,
|
|
7308
7432
|
this.entityConstructor,
|
|
7309
|
-
includeTree
|
|
7433
|
+
includeTree,
|
|
7434
|
+
cacheContext
|
|
7310
7435
|
);
|
|
7311
7436
|
}
|
|
7312
7437
|
/**
|
|
@@ -7728,10 +7853,43 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7728
7853
|
}
|
|
7729
7854
|
return this;
|
|
7730
7855
|
}
|
|
7856
|
+
/**
|
|
7857
|
+
* Configures caching for this query.
|
|
7858
|
+
* @param key - Unique cache key
|
|
7859
|
+
* @param ttl - Time-to-live (e.g., '1h', '30m', '1d') or milliseconds
|
|
7860
|
+
* @param tagsOrConfig - Optional tags for invalidation or configuration object
|
|
7861
|
+
* @returns New query builder instance with cache configuration
|
|
7862
|
+
* @example
|
|
7863
|
+
* // Simple cache with TTL
|
|
7864
|
+
* await selectFrom(User).cache('active_users', '1h').execute(session);
|
|
7865
|
+
*
|
|
7866
|
+
* // Cache with tags for invalidation
|
|
7867
|
+
* await selectFrom(User)
|
|
7868
|
+
* .cache('users_list', '30m', ['users', 'dashboard'])
|
|
7869
|
+
* .execute(session);
|
|
7870
|
+
*
|
|
7871
|
+
* // Cache with auto-invalidation
|
|
7872
|
+
* await selectFrom(User)
|
|
7873
|
+
* .cache('users_list', '1h', { autoInvalidate: true })
|
|
7874
|
+
* .execute(session);
|
|
7875
|
+
*/
|
|
7876
|
+
cache(key, ttl, tagsOrConfig) {
|
|
7877
|
+
const options = CacheFacet.createOptions(key, ttl, tagsOrConfig);
|
|
7878
|
+
const nextCacheContext = this.cacheFacet.cache(this.cacheContext, options);
|
|
7879
|
+
const builder = this.clone(
|
|
7880
|
+
this.context,
|
|
7881
|
+
new Set(this.lazyRelations),
|
|
7882
|
+
new Map(this.lazyRelationOptions),
|
|
7883
|
+
this.includeTree,
|
|
7884
|
+
nextCacheContext
|
|
7885
|
+
);
|
|
7886
|
+
return builder;
|
|
7887
|
+
}
|
|
7731
7888
|
/**
|
|
7732
7889
|
* Executes the query and returns hydrated results.
|
|
7733
7890
|
* If the builder was created with an entity constructor (e.g. via selectFromEntity),
|
|
7734
7891
|
* this will automatically return fully materialized entity instances.
|
|
7892
|
+
* If caching is configured, results will be cached/retrieved from cache.
|
|
7735
7893
|
*
|
|
7736
7894
|
* @param ctx - ORM session context
|
|
7737
7895
|
* @returns Promise of entity instances (or objects if generic T is not an entity)
|
|
@@ -7741,6 +7899,20 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7741
7899
|
* users[0] instanceof User; // true
|
|
7742
7900
|
*/
|
|
7743
7901
|
async execute(ctx) {
|
|
7902
|
+
const cacheOptions = this.cacheFacet.getOptions(this.cacheContext);
|
|
7903
|
+
if (!cacheOptions || !ctx.cacheManager) {
|
|
7904
|
+
return this.executeWithoutCache(ctx);
|
|
7905
|
+
}
|
|
7906
|
+
return ctx.cacheManager.getOrExecute(
|
|
7907
|
+
cacheOptions,
|
|
7908
|
+
() => this.executeWithoutCache(ctx),
|
|
7909
|
+
ctx.tenantId
|
|
7910
|
+
);
|
|
7911
|
+
}
|
|
7912
|
+
/**
|
|
7913
|
+
* Executa a query sem cache (método interno)
|
|
7914
|
+
*/
|
|
7915
|
+
async executeWithoutCache(ctx) {
|
|
7744
7916
|
if (this.entityConstructor) {
|
|
7745
7917
|
return this.executeAs(this.entityConstructor, ctx);
|
|
7746
7918
|
}
|
|
@@ -12062,12 +12234,13 @@ var UnitOfWork = class {
|
|
|
12062
12234
|
await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
|
|
12063
12235
|
const payload = this.extractColumns(tracked.table, tracked.entity);
|
|
12064
12236
|
let builder = new InsertQueryBuilder(tracked.table).values(payload);
|
|
12065
|
-
if (this.dialect.
|
|
12237
|
+
if (this.dialect.supportsDmlReturningClause()) {
|
|
12066
12238
|
builder = builder.returning(...this.getReturningColumns(tracked.table));
|
|
12067
12239
|
}
|
|
12068
12240
|
const compiled = builder.compile(this.dialect);
|
|
12069
12241
|
const results = await this.executeCompiled(compiled);
|
|
12070
12242
|
this.applyReturningResults(tracked, results);
|
|
12243
|
+
this.applyInsertedIdIfAbsent(tracked, results);
|
|
12071
12244
|
tracked.status = "managed" /* Managed */;
|
|
12072
12245
|
tracked.original = this.createSnapshot(tracked.table, tracked.entity);
|
|
12073
12246
|
tracked.pk = this.getPrimaryKeyValue(tracked);
|
|
@@ -12089,7 +12262,7 @@ var UnitOfWork = class {
|
|
|
12089
12262
|
const pkColumn = tracked.table.columns[findPrimaryKey(tracked.table)];
|
|
12090
12263
|
if (!pkColumn) return;
|
|
12091
12264
|
let builder = new UpdateQueryBuilder(tracked.table).set(changes).where(eq(pkColumn, tracked.pk));
|
|
12092
|
-
if (this.dialect.
|
|
12265
|
+
if (this.dialect.supportsDmlReturningClause()) {
|
|
12093
12266
|
builder = builder.returning(...this.getReturningColumns(tracked.table));
|
|
12094
12267
|
}
|
|
12095
12268
|
const compiled = builder.compile(this.dialect);
|
|
@@ -12183,9 +12356,8 @@ var UnitOfWork = class {
|
|
|
12183
12356
|
* @param results - Query results
|
|
12184
12357
|
*/
|
|
12185
12358
|
applyReturningResults(tracked, results) {
|
|
12186
|
-
if (!this.dialect.supportsReturning()) return;
|
|
12187
12359
|
const first = results[0];
|
|
12188
|
-
if (!first || first.values.length === 0) return;
|
|
12360
|
+
if (!first || first.columns.length === 0 || first.values.length === 0) return;
|
|
12189
12361
|
const row = first.values[0];
|
|
12190
12362
|
for (let i = 0; i < first.columns.length; i++) {
|
|
12191
12363
|
const columnName = this.normalizeColumnName(first.columns[i]);
|
|
@@ -12193,6 +12365,21 @@ var UnitOfWork = class {
|
|
|
12193
12365
|
tracked.entity[columnName] = row[i];
|
|
12194
12366
|
}
|
|
12195
12367
|
}
|
|
12368
|
+
/**
|
|
12369
|
+
* Applies the driver-provided insertId when no RETURNING clause was used.
|
|
12370
|
+
* Only sets the PK if it is currently absent on the entity.
|
|
12371
|
+
* @param tracked - The tracked entity
|
|
12372
|
+
* @param results - Query results (may contain meta.insertId)
|
|
12373
|
+
*/
|
|
12374
|
+
applyInsertedIdIfAbsent(tracked, results) {
|
|
12375
|
+
const pkName = findPrimaryKey(tracked.table);
|
|
12376
|
+
const current = tracked.entity[pkName];
|
|
12377
|
+
if (current != null) return;
|
|
12378
|
+
const first = results[0];
|
|
12379
|
+
const insertId = first?.meta?.insertId;
|
|
12380
|
+
if (insertId == null) return;
|
|
12381
|
+
tracked.entity[pkName] = insertId;
|
|
12382
|
+
}
|
|
12196
12383
|
/**
|
|
12197
12384
|
* Normalizes a column name by removing quotes and table prefixes.
|
|
12198
12385
|
* @param column - The column name to normalize
|
|
@@ -12830,6 +13017,10 @@ var OrmSession = class {
|
|
|
12830
13017
|
domainEvents;
|
|
12831
13018
|
/** The relation change processor */
|
|
12832
13019
|
relationChanges;
|
|
13020
|
+
/** The cache manager for query caching */
|
|
13021
|
+
cacheManager;
|
|
13022
|
+
/** The tenant ID for multi-tenancy support */
|
|
13023
|
+
tenantId;
|
|
12833
13024
|
interceptors;
|
|
12834
13025
|
saveGraphDefaults;
|
|
12835
13026
|
/**
|
|
@@ -12844,6 +13035,8 @@ var OrmSession = class {
|
|
|
12844
13035
|
this.unitOfWork = new UnitOfWork(this.orm.dialect, this.executor, this.identityMap, () => this);
|
|
12845
13036
|
this.relationChanges = new RelationChangeProcessor(this.unitOfWork, this.orm.dialect, this.executor);
|
|
12846
13037
|
this.domainEvents = new DomainEventBus(opts.domainEventHandlers);
|
|
13038
|
+
this.cacheManager = opts.cacheManager;
|
|
13039
|
+
this.tenantId = opts.tenantId;
|
|
12847
13040
|
}
|
|
12848
13041
|
/**
|
|
12849
13042
|
* Releases resources associated with this session (executor/pool leases) and resets tracking.
|
|
@@ -13218,6 +13411,45 @@ var OrmSession = class {
|
|
|
13218
13411
|
entityContext: this
|
|
13219
13412
|
};
|
|
13220
13413
|
}
|
|
13414
|
+
/**
|
|
13415
|
+
* Invalidates cache by specific tags.
|
|
13416
|
+
* @param tags - Tags to invalidate
|
|
13417
|
+
* @throws Error if no cache manager is configured
|
|
13418
|
+
* @example
|
|
13419
|
+
* await session.invalidateCacheTags(['users', 'dashboard']);
|
|
13420
|
+
*/
|
|
13421
|
+
async invalidateCacheTags(tags) {
|
|
13422
|
+
if (!this.cacheManager) {
|
|
13423
|
+
throw new Error("No cache manager configured. Please provide cacheManager when creating the session.");
|
|
13424
|
+
}
|
|
13425
|
+
await this.cacheManager.invalidateTags(tags);
|
|
13426
|
+
}
|
|
13427
|
+
/**
|
|
13428
|
+
* Invalidates cache by key prefix (useful for multi-tenancy).
|
|
13429
|
+
* @param prefix - Prefix to match cache keys
|
|
13430
|
+
* @throws Error if no cache manager is configured
|
|
13431
|
+
* @example
|
|
13432
|
+
* await session.invalidateCachePrefix('tenant:123:');
|
|
13433
|
+
*/
|
|
13434
|
+
async invalidateCachePrefix(prefix) {
|
|
13435
|
+
if (!this.cacheManager) {
|
|
13436
|
+
throw new Error("No cache manager configured. Please provide cacheManager when creating the session.");
|
|
13437
|
+
}
|
|
13438
|
+
await this.cacheManager.invalidatePrefix(prefix);
|
|
13439
|
+
}
|
|
13440
|
+
/**
|
|
13441
|
+
* Invalidates a specific cache key.
|
|
13442
|
+
* @param key - Cache key to invalidate
|
|
13443
|
+
* @throws Error if no cache manager is configured
|
|
13444
|
+
* @example
|
|
13445
|
+
* await session.invalidateCacheKey('active_users');
|
|
13446
|
+
*/
|
|
13447
|
+
async invalidateCacheKey(key) {
|
|
13448
|
+
if (!this.cacheManager) {
|
|
13449
|
+
throw new Error("No cache manager configured. Please provide cacheManager when creating the session.");
|
|
13450
|
+
}
|
|
13451
|
+
await this.cacheManager.invalidateKey(key, this.tenantId);
|
|
13452
|
+
}
|
|
13221
13453
|
/**
|
|
13222
13454
|
* Merges session defaults with per-call saveGraph options.
|
|
13223
13455
|
* @param options - Per-call saveGraph options
|
|
@@ -13255,6 +13487,309 @@ var InterceptorPipeline = class {
|
|
|
13255
13487
|
}
|
|
13256
13488
|
};
|
|
13257
13489
|
|
|
13490
|
+
// src/cache/strategies/default-cache-strategy.ts
|
|
13491
|
+
var DefaultCacheStrategy = class {
|
|
13492
|
+
name = "default";
|
|
13493
|
+
/**
|
|
13494
|
+
* Gera chave de cache com prefixo de tenant se houver
|
|
13495
|
+
*/
|
|
13496
|
+
generateKey(queryKey, tenantId) {
|
|
13497
|
+
if (tenantId !== void 0) {
|
|
13498
|
+
return `tenant:${tenantId}:${queryKey}`;
|
|
13499
|
+
}
|
|
13500
|
+
return queryKey;
|
|
13501
|
+
}
|
|
13502
|
+
/**
|
|
13503
|
+
* Verifica se deve cachear baseado na condição configurada
|
|
13504
|
+
*/
|
|
13505
|
+
shouldCache(result, options) {
|
|
13506
|
+
if (options.condition) {
|
|
13507
|
+
return options.condition(result);
|
|
13508
|
+
}
|
|
13509
|
+
return true;
|
|
13510
|
+
}
|
|
13511
|
+
/**
|
|
13512
|
+
* Serializa com suporte a tipos especiais
|
|
13513
|
+
*/
|
|
13514
|
+
serialize(data) {
|
|
13515
|
+
return JSON.stringify(data, (key, value) => {
|
|
13516
|
+
if (value instanceof Date) {
|
|
13517
|
+
return { __type: "Date", value: value.toISOString() };
|
|
13518
|
+
}
|
|
13519
|
+
if (typeof value === "bigint") {
|
|
13520
|
+
return { __type: "BigInt", value: value.toString() };
|
|
13521
|
+
}
|
|
13522
|
+
if (value instanceof Map) {
|
|
13523
|
+
return { __type: "Map", value: Array.from(value.entries()) };
|
|
13524
|
+
}
|
|
13525
|
+
if (value instanceof Set) {
|
|
13526
|
+
return { __type: "Set", value: Array.from(value) };
|
|
13527
|
+
}
|
|
13528
|
+
return value;
|
|
13529
|
+
});
|
|
13530
|
+
}
|
|
13531
|
+
/**
|
|
13532
|
+
* Desserializa restaurando tipos especiais
|
|
13533
|
+
*/
|
|
13534
|
+
deserialize(data) {
|
|
13535
|
+
if (typeof data !== "string") {
|
|
13536
|
+
return data;
|
|
13537
|
+
}
|
|
13538
|
+
return JSON.parse(data, (key, value) => {
|
|
13539
|
+
if (!value || typeof value !== "object") {
|
|
13540
|
+
return value;
|
|
13541
|
+
}
|
|
13542
|
+
if (value.__type === "Date") {
|
|
13543
|
+
return new Date(value.value);
|
|
13544
|
+
}
|
|
13545
|
+
if (value.__type === "BigInt") {
|
|
13546
|
+
return BigInt(value.value);
|
|
13547
|
+
}
|
|
13548
|
+
if (value.__type === "Map") {
|
|
13549
|
+
return new Map(value.value);
|
|
13550
|
+
}
|
|
13551
|
+
if (value.__type === "Set") {
|
|
13552
|
+
return new Set(value.value);
|
|
13553
|
+
}
|
|
13554
|
+
return value;
|
|
13555
|
+
});
|
|
13556
|
+
}
|
|
13557
|
+
};
|
|
13558
|
+
|
|
13559
|
+
// src/cache/duration-utils.ts
|
|
13560
|
+
var DURATION_MULTIPLIERS = {
|
|
13561
|
+
s: 1e3,
|
|
13562
|
+
// segundos
|
|
13563
|
+
m: 6e4,
|
|
13564
|
+
// minutos
|
|
13565
|
+
h: 36e5,
|
|
13566
|
+
// horas
|
|
13567
|
+
d: 864e5,
|
|
13568
|
+
// dias
|
|
13569
|
+
w: 6048e5
|
|
13570
|
+
// semanas
|
|
13571
|
+
};
|
|
13572
|
+
function parseDuration(duration) {
|
|
13573
|
+
if (typeof duration === "number") {
|
|
13574
|
+
return duration;
|
|
13575
|
+
}
|
|
13576
|
+
const match = duration.match(/^(\d+)([smhdw])$/);
|
|
13577
|
+
if (!match) {
|
|
13578
|
+
throw new Error(
|
|
13579
|
+
`Invalid duration format: "${duration}". Use formats like '30s', '10m', '2h', '1d', '1w' or a number in milliseconds.`
|
|
13580
|
+
);
|
|
13581
|
+
}
|
|
13582
|
+
const value = parseInt(match[1], 10);
|
|
13583
|
+
const unit = match[2];
|
|
13584
|
+
return value * DURATION_MULTIPLIERS[unit];
|
|
13585
|
+
}
|
|
13586
|
+
function formatDuration(ms) {
|
|
13587
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
13588
|
+
if (ms < 6e4) return `${Math.floor(ms / 1e3)}s`;
|
|
13589
|
+
if (ms < 36e5) return `${Math.floor(ms / 6e4)}m`;
|
|
13590
|
+
if (ms < 864e5) return `${Math.floor(ms / 36e5)}h`;
|
|
13591
|
+
if (ms < 6048e5) return `${Math.floor(ms / 864e5)}d`;
|
|
13592
|
+
return `${Math.floor(ms / 6048e5)}w`;
|
|
13593
|
+
}
|
|
13594
|
+
function isValidDuration(value) {
|
|
13595
|
+
if (typeof value === "number") {
|
|
13596
|
+
return value >= 0;
|
|
13597
|
+
}
|
|
13598
|
+
if (typeof value === "string") {
|
|
13599
|
+
return /^\d+[smhdw]$/.test(value);
|
|
13600
|
+
}
|
|
13601
|
+
return false;
|
|
13602
|
+
}
|
|
13603
|
+
|
|
13604
|
+
// src/cache/adapters/memory-cache-adapter.ts
|
|
13605
|
+
var MemoryCacheAdapter = class {
|
|
13606
|
+
name = "memory";
|
|
13607
|
+
storage = /* @__PURE__ */ new Map();
|
|
13608
|
+
tagIndex = /* @__PURE__ */ new Map();
|
|
13609
|
+
async get(key) {
|
|
13610
|
+
const entry = this.storage.get(key);
|
|
13611
|
+
if (!entry) {
|
|
13612
|
+
return void 0;
|
|
13613
|
+
}
|
|
13614
|
+
if (entry.expiresAt && Date.now() > entry.expiresAt) {
|
|
13615
|
+
await this.delete(key);
|
|
13616
|
+
return void 0;
|
|
13617
|
+
}
|
|
13618
|
+
return entry.value;
|
|
13619
|
+
}
|
|
13620
|
+
async has(key) {
|
|
13621
|
+
const value = await this.get(key);
|
|
13622
|
+
return value !== void 0;
|
|
13623
|
+
}
|
|
13624
|
+
async set(key, value, ttlMs) {
|
|
13625
|
+
const entry = {
|
|
13626
|
+
value,
|
|
13627
|
+
expiresAt: ttlMs ? Date.now() + ttlMs : void 0
|
|
13628
|
+
};
|
|
13629
|
+
this.storage.set(key, entry);
|
|
13630
|
+
}
|
|
13631
|
+
async delete(key) {
|
|
13632
|
+
this.storage.delete(key);
|
|
13633
|
+
for (const [tag, keys] of this.tagIndex) {
|
|
13634
|
+
keys.delete(key);
|
|
13635
|
+
if (keys.size === 0) {
|
|
13636
|
+
this.tagIndex.delete(tag);
|
|
13637
|
+
}
|
|
13638
|
+
}
|
|
13639
|
+
}
|
|
13640
|
+
async invalidate(key) {
|
|
13641
|
+
await this.delete(key);
|
|
13642
|
+
}
|
|
13643
|
+
async invalidateTags(tags) {
|
|
13644
|
+
const keysToDelete = /* @__PURE__ */ new Set();
|
|
13645
|
+
for (const tag of tags) {
|
|
13646
|
+
const keys = this.tagIndex.get(tag);
|
|
13647
|
+
if (keys) {
|
|
13648
|
+
for (const key of keys) {
|
|
13649
|
+
keysToDelete.add(key);
|
|
13650
|
+
}
|
|
13651
|
+
this.tagIndex.delete(tag);
|
|
13652
|
+
}
|
|
13653
|
+
}
|
|
13654
|
+
for (const key of keysToDelete) {
|
|
13655
|
+
this.storage.delete(key);
|
|
13656
|
+
}
|
|
13657
|
+
}
|
|
13658
|
+
async invalidatePrefix(prefix) {
|
|
13659
|
+
const keysToDelete = [];
|
|
13660
|
+
for (const key of this.storage.keys()) {
|
|
13661
|
+
if (key.startsWith(prefix)) {
|
|
13662
|
+
keysToDelete.push(key);
|
|
13663
|
+
}
|
|
13664
|
+
}
|
|
13665
|
+
for (const key of keysToDelete) {
|
|
13666
|
+
await this.delete(key);
|
|
13667
|
+
}
|
|
13668
|
+
}
|
|
13669
|
+
/**
|
|
13670
|
+
* Registra uma chave com tags (para invalidação)
|
|
13671
|
+
*/
|
|
13672
|
+
registerTags(key, tags) {
|
|
13673
|
+
for (const tag of tags) {
|
|
13674
|
+
if (!this.tagIndex.has(tag)) {
|
|
13675
|
+
this.tagIndex.set(tag, /* @__PURE__ */ new Set());
|
|
13676
|
+
}
|
|
13677
|
+
this.tagIndex.get(tag).add(key);
|
|
13678
|
+
}
|
|
13679
|
+
}
|
|
13680
|
+
/**
|
|
13681
|
+
* Limpa todo o cache
|
|
13682
|
+
*/
|
|
13683
|
+
clear() {
|
|
13684
|
+
this.storage.clear();
|
|
13685
|
+
this.tagIndex.clear();
|
|
13686
|
+
}
|
|
13687
|
+
/**
|
|
13688
|
+
* Retorna estatísticas do cache
|
|
13689
|
+
*/
|
|
13690
|
+
getStats() {
|
|
13691
|
+
return {
|
|
13692
|
+
size: this.storage.size,
|
|
13693
|
+
tags: this.tagIndex.size
|
|
13694
|
+
};
|
|
13695
|
+
}
|
|
13696
|
+
async dispose() {
|
|
13697
|
+
this.clear();
|
|
13698
|
+
}
|
|
13699
|
+
};
|
|
13700
|
+
|
|
13701
|
+
// src/cache/query-cache-manager.ts
|
|
13702
|
+
var QueryCacheManager = class {
|
|
13703
|
+
constructor(provider = new MemoryCacheAdapter(), strategy = new DefaultCacheStrategy(), defaultTtl = "1h") {
|
|
13704
|
+
this.provider = provider;
|
|
13705
|
+
this.strategy = strategy;
|
|
13706
|
+
this.defaultTtl = defaultTtl;
|
|
13707
|
+
}
|
|
13708
|
+
/**
|
|
13709
|
+
* Executa com cache - padrão execute-around
|
|
13710
|
+
* @returns Resultado da execução (do cache ou da função)
|
|
13711
|
+
*/
|
|
13712
|
+
async getOrExecute(options, executor, tenantId) {
|
|
13713
|
+
const key = this.strategy.generateKey(options.key, tenantId);
|
|
13714
|
+
const ttlMs = this.parseDuration(options.ttl ?? this.defaultTtl);
|
|
13715
|
+
const cached = await this.provider.get(key);
|
|
13716
|
+
if (cached !== void 0) {
|
|
13717
|
+
return this.strategy.deserialize(cached);
|
|
13718
|
+
}
|
|
13719
|
+
const result = await executor();
|
|
13720
|
+
if (!this.strategy.shouldCache(result, options)) {
|
|
13721
|
+
return result;
|
|
13722
|
+
}
|
|
13723
|
+
const serialized = this.strategy.serialize(result);
|
|
13724
|
+
await this.provider.set(key, serialized, ttlMs);
|
|
13725
|
+
if (options.tags) {
|
|
13726
|
+
await this.registerTags(key, options.tags);
|
|
13727
|
+
}
|
|
13728
|
+
return result;
|
|
13729
|
+
}
|
|
13730
|
+
/**
|
|
13731
|
+
* Invalida uma chave específica
|
|
13732
|
+
*/
|
|
13733
|
+
async invalidateKey(key, tenantId) {
|
|
13734
|
+
const fullKey = this.strategy.generateKey(key, tenantId);
|
|
13735
|
+
await this.provider.invalidate(fullKey);
|
|
13736
|
+
}
|
|
13737
|
+
/**
|
|
13738
|
+
* Invalida por tags
|
|
13739
|
+
*/
|
|
13740
|
+
async invalidateTags(tags) {
|
|
13741
|
+
await this.provider.invalidateTags(tags);
|
|
13742
|
+
}
|
|
13743
|
+
/**
|
|
13744
|
+
* Invalida por prefixo (útil para multi-tenancy)
|
|
13745
|
+
*/
|
|
13746
|
+
async invalidatePrefix(prefix) {
|
|
13747
|
+
await this.provider.invalidatePrefix(prefix);
|
|
13748
|
+
}
|
|
13749
|
+
/**
|
|
13750
|
+
* Limpa todo o cache (cuidado!)
|
|
13751
|
+
*/
|
|
13752
|
+
async clear() {
|
|
13753
|
+
const provider = this.provider;
|
|
13754
|
+
if (typeof provider.clear === "function") {
|
|
13755
|
+
provider.clear();
|
|
13756
|
+
} else {
|
|
13757
|
+
throw new Error("Cache provider does not support clear operation");
|
|
13758
|
+
}
|
|
13759
|
+
}
|
|
13760
|
+
/**
|
|
13761
|
+
* Retorna estatísticas do cache (se disponível)
|
|
13762
|
+
*/
|
|
13763
|
+
getStats() {
|
|
13764
|
+
const provider = this.provider;
|
|
13765
|
+
if (typeof provider.getStats === "function") {
|
|
13766
|
+
return provider.getStats();
|
|
13767
|
+
}
|
|
13768
|
+
return void 0;
|
|
13769
|
+
}
|
|
13770
|
+
/**
|
|
13771
|
+
* Libera recursos do cache
|
|
13772
|
+
*/
|
|
13773
|
+
async dispose() {
|
|
13774
|
+
await this.provider.dispose?.();
|
|
13775
|
+
}
|
|
13776
|
+
/**
|
|
13777
|
+
* Registra tags para uma chave
|
|
13778
|
+
*/
|
|
13779
|
+
async registerTags(key, tags) {
|
|
13780
|
+
const provider = this.provider;
|
|
13781
|
+
if (typeof provider.registerTags === "function") {
|
|
13782
|
+
provider.registerTags(key, tags);
|
|
13783
|
+
}
|
|
13784
|
+
}
|
|
13785
|
+
/**
|
|
13786
|
+
* Converte duração para milissegundos
|
|
13787
|
+
*/
|
|
13788
|
+
parseDuration(d) {
|
|
13789
|
+
return parseDuration(d);
|
|
13790
|
+
}
|
|
13791
|
+
};
|
|
13792
|
+
|
|
13258
13793
|
// src/orm/orm.ts
|
|
13259
13794
|
var Orm = class {
|
|
13260
13795
|
/** The database dialect */
|
|
@@ -13263,6 +13798,8 @@ var Orm = class {
|
|
|
13263
13798
|
interceptors;
|
|
13264
13799
|
/** The naming strategy */
|
|
13265
13800
|
namingStrategy;
|
|
13801
|
+
/** The cache manager (if configured) */
|
|
13802
|
+
cacheManager;
|
|
13266
13803
|
executorFactory;
|
|
13267
13804
|
/**
|
|
13268
13805
|
* Creates a new ORM instance.
|
|
@@ -13273,15 +13810,27 @@ var Orm = class {
|
|
|
13273
13810
|
this.interceptors = opts.interceptors ?? new InterceptorPipeline();
|
|
13274
13811
|
this.namingStrategy = opts.namingStrategy ?? new DefaultNamingStrategy();
|
|
13275
13812
|
this.executorFactory = opts.executorFactory;
|
|
13813
|
+
if (opts.cache) {
|
|
13814
|
+
this.cacheManager = new QueryCacheManager(
|
|
13815
|
+
opts.cache.provider,
|
|
13816
|
+
opts.cache.strategy ?? new DefaultCacheStrategy(),
|
|
13817
|
+
opts.cache.defaultTtl ?? "1h"
|
|
13818
|
+
);
|
|
13819
|
+
}
|
|
13276
13820
|
}
|
|
13277
13821
|
/**
|
|
13278
13822
|
* Creates a new ORM session.
|
|
13279
|
-
* @param options - Optional session options
|
|
13823
|
+
* @param options - Optional session options (e.g., tenantId for multi-tenancy)
|
|
13280
13824
|
* @returns The ORM session
|
|
13281
13825
|
*/
|
|
13282
|
-
createSession() {
|
|
13826
|
+
createSession(options) {
|
|
13283
13827
|
const executor = this.executorFactory.createExecutor();
|
|
13284
|
-
return new OrmSession({
|
|
13828
|
+
return new OrmSession({
|
|
13829
|
+
orm: this,
|
|
13830
|
+
executor,
|
|
13831
|
+
cacheManager: this.cacheManager,
|
|
13832
|
+
tenantId: options?.tenantId
|
|
13833
|
+
});
|
|
13285
13834
|
}
|
|
13286
13835
|
/**
|
|
13287
13836
|
* Executes a function within a transaction.
|
|
@@ -13292,7 +13841,11 @@ var Orm = class {
|
|
|
13292
13841
|
*/
|
|
13293
13842
|
async transaction(fn8) {
|
|
13294
13843
|
const executor = this.executorFactory.createTransactionalExecutor();
|
|
13295
|
-
const session = new OrmSession({
|
|
13844
|
+
const session = new OrmSession({
|
|
13845
|
+
orm: this,
|
|
13846
|
+
executor,
|
|
13847
|
+
cacheManager: this.cacheManager
|
|
13848
|
+
});
|
|
13296
13849
|
try {
|
|
13297
13850
|
return await session.transaction(() => fn8(session));
|
|
13298
13851
|
} finally {
|
|
@@ -14457,7 +15010,15 @@ function createMysqlExecutor(client) {
|
|
|
14457
15010
|
async executeSql(sql, params) {
|
|
14458
15011
|
const [rows] = await client.query(sql, params);
|
|
14459
15012
|
if (!Array.isArray(rows)) {
|
|
14460
|
-
|
|
15013
|
+
const header = rows;
|
|
15014
|
+
return [{
|
|
15015
|
+
columns: [],
|
|
15016
|
+
values: [],
|
|
15017
|
+
meta: {
|
|
15018
|
+
insertId: header.insertId,
|
|
15019
|
+
rowsAffected: header.affectedRows
|
|
15020
|
+
}
|
|
15021
|
+
}];
|
|
14461
15022
|
}
|
|
14462
15023
|
const result = rowsToQueryResult(
|
|
14463
15024
|
rows
|
|
@@ -17766,6 +18327,162 @@ function queryResultsToRows(results) {
|
|
|
17766
18327
|
}
|
|
17767
18328
|
return rows;
|
|
17768
18329
|
}
|
|
18330
|
+
|
|
18331
|
+
// src/cache/adapters/keyv-cache-adapter.ts
|
|
18332
|
+
var KeyvCacheAdapter = class {
|
|
18333
|
+
constructor(keyv) {
|
|
18334
|
+
this.keyv = keyv;
|
|
18335
|
+
}
|
|
18336
|
+
name = "keyv";
|
|
18337
|
+
async get(key) {
|
|
18338
|
+
return this.keyv.get(key);
|
|
18339
|
+
}
|
|
18340
|
+
async has(key) {
|
|
18341
|
+
const value = await this.keyv.get(key);
|
|
18342
|
+
return value !== void 0;
|
|
18343
|
+
}
|
|
18344
|
+
async set(key, value, ttlMs) {
|
|
18345
|
+
await this.keyv.set(key, value, ttlMs);
|
|
18346
|
+
}
|
|
18347
|
+
async delete(key) {
|
|
18348
|
+
await this.keyv.delete(key);
|
|
18349
|
+
}
|
|
18350
|
+
async invalidate(key) {
|
|
18351
|
+
await this.delete(key);
|
|
18352
|
+
}
|
|
18353
|
+
async invalidateTags(_tags) {
|
|
18354
|
+
throw new Error(
|
|
18355
|
+
"Keyv adapter does not support tag invalidation. Use MemoryCacheAdapter for testing or implement a custom Redis provider."
|
|
18356
|
+
);
|
|
18357
|
+
}
|
|
18358
|
+
async invalidatePrefix(prefix) {
|
|
18359
|
+
if (typeof this.keyv.iterator === "function") {
|
|
18360
|
+
const keys = [];
|
|
18361
|
+
for await (const [key] of this.keyv.iterator()) {
|
|
18362
|
+
if (key.startsWith(prefix)) {
|
|
18363
|
+
keys.push(key);
|
|
18364
|
+
}
|
|
18365
|
+
}
|
|
18366
|
+
if (keys.length > 0) {
|
|
18367
|
+
await Promise.all(keys.map((k) => this.keyv.delete(k)));
|
|
18368
|
+
}
|
|
18369
|
+
return;
|
|
18370
|
+
}
|
|
18371
|
+
throw new Error(
|
|
18372
|
+
"Keyv adapter does not support prefix invalidation in this store. Consider using a store with iterator support."
|
|
18373
|
+
);
|
|
18374
|
+
}
|
|
18375
|
+
async dispose() {
|
|
18376
|
+
await this.keyv.disconnect?.();
|
|
18377
|
+
}
|
|
18378
|
+
};
|
|
18379
|
+
|
|
18380
|
+
// src/cache/tag-index.ts
|
|
18381
|
+
var TagIndex = class {
|
|
18382
|
+
tagToKeys = /* @__PURE__ */ new Map();
|
|
18383
|
+
keyToTags = /* @__PURE__ */ new Map();
|
|
18384
|
+
/**
|
|
18385
|
+
* Registra que uma chave pertence a determinadas tags
|
|
18386
|
+
*/
|
|
18387
|
+
register(key, tags) {
|
|
18388
|
+
for (const tag of tags) {
|
|
18389
|
+
if (!this.tagToKeys.has(tag)) {
|
|
18390
|
+
this.tagToKeys.set(tag, /* @__PURE__ */ new Set());
|
|
18391
|
+
}
|
|
18392
|
+
this.tagToKeys.get(tag).add(key);
|
|
18393
|
+
}
|
|
18394
|
+
const existingTags = this.keyToTags.get(key) ?? /* @__PURE__ */ new Set();
|
|
18395
|
+
tags.forEach((tag) => existingTags.add(tag));
|
|
18396
|
+
this.keyToTags.set(key, existingTags);
|
|
18397
|
+
}
|
|
18398
|
+
/**
|
|
18399
|
+
* Remove uma chave do índice
|
|
18400
|
+
*/
|
|
18401
|
+
unregister(key) {
|
|
18402
|
+
const tags = this.keyToTags.get(key);
|
|
18403
|
+
if (tags) {
|
|
18404
|
+
for (const tag of tags) {
|
|
18405
|
+
this.tagToKeys.get(tag)?.delete(key);
|
|
18406
|
+
if (this.tagToKeys.get(tag)?.size === 0) {
|
|
18407
|
+
this.tagToKeys.delete(tag);
|
|
18408
|
+
}
|
|
18409
|
+
}
|
|
18410
|
+
this.keyToTags.delete(key);
|
|
18411
|
+
}
|
|
18412
|
+
}
|
|
18413
|
+
/**
|
|
18414
|
+
* Obtém todas as chaves de uma tag
|
|
18415
|
+
*/
|
|
18416
|
+
getKeysByTag(tag) {
|
|
18417
|
+
return Array.from(this.tagToKeys.get(tag) ?? []);
|
|
18418
|
+
}
|
|
18419
|
+
/**
|
|
18420
|
+
* Obtém todas as tags de uma chave
|
|
18421
|
+
*/
|
|
18422
|
+
getTagsByKey(key) {
|
|
18423
|
+
return Array.from(this.keyToTags.get(key) ?? []);
|
|
18424
|
+
}
|
|
18425
|
+
/**
|
|
18426
|
+
* Invalida todas as chaves de um conjunto de tags
|
|
18427
|
+
* Retorna as chaves afetadas
|
|
18428
|
+
*/
|
|
18429
|
+
invalidateTags(tags) {
|
|
18430
|
+
const keysToInvalidate = /* @__PURE__ */ new Set();
|
|
18431
|
+
for (const tag of tags) {
|
|
18432
|
+
const keys = this.tagToKeys.get(tag);
|
|
18433
|
+
if (keys) {
|
|
18434
|
+
for (const key of keys) {
|
|
18435
|
+
keysToInvalidate.add(key);
|
|
18436
|
+
this.unregister(key);
|
|
18437
|
+
}
|
|
18438
|
+
this.tagToKeys.delete(tag);
|
|
18439
|
+
}
|
|
18440
|
+
}
|
|
18441
|
+
return Array.from(keysToInvalidate);
|
|
18442
|
+
}
|
|
18443
|
+
/**
|
|
18444
|
+
* Invalida por prefixo (útil para multi-tenancy)
|
|
18445
|
+
* Retorna as chaves afetadas
|
|
18446
|
+
*/
|
|
18447
|
+
invalidatePrefix(prefix) {
|
|
18448
|
+
const keysToInvalidate = [];
|
|
18449
|
+
for (const key of this.keyToTags.keys()) {
|
|
18450
|
+
if (key.startsWith(prefix)) {
|
|
18451
|
+
keysToInvalidate.push(key);
|
|
18452
|
+
this.unregister(key);
|
|
18453
|
+
}
|
|
18454
|
+
}
|
|
18455
|
+
return keysToInvalidate;
|
|
18456
|
+
}
|
|
18457
|
+
/**
|
|
18458
|
+
* Retorna todas as tags registradas
|
|
18459
|
+
*/
|
|
18460
|
+
getAllTags() {
|
|
18461
|
+
return Array.from(this.tagToKeys.keys());
|
|
18462
|
+
}
|
|
18463
|
+
/**
|
|
18464
|
+
* Retorna todas as chaves registradas
|
|
18465
|
+
*/
|
|
18466
|
+
getAllKeys() {
|
|
18467
|
+
return Array.from(this.keyToTags.keys());
|
|
18468
|
+
}
|
|
18469
|
+
/**
|
|
18470
|
+
* Limpa todo o índice
|
|
18471
|
+
*/
|
|
18472
|
+
clear() {
|
|
18473
|
+
this.tagToKeys.clear();
|
|
18474
|
+
this.keyToTags.clear();
|
|
18475
|
+
}
|
|
18476
|
+
/**
|
|
18477
|
+
* Retorna estatísticas do índice
|
|
18478
|
+
*/
|
|
18479
|
+
getStats() {
|
|
18480
|
+
return {
|
|
18481
|
+
tags: this.tagToKeys.size,
|
|
18482
|
+
keys: this.keyToTags.size
|
|
18483
|
+
};
|
|
18484
|
+
}
|
|
18485
|
+
};
|
|
17769
18486
|
export {
|
|
17770
18487
|
Alphanumeric,
|
|
17771
18488
|
AsyncLocalStorage,
|
|
@@ -17784,6 +18501,7 @@ export {
|
|
|
17784
18501
|
DateTimeTypeStrategy,
|
|
17785
18502
|
DecimalTypeStrategy,
|
|
17786
18503
|
DefaultBelongsToReference,
|
|
18504
|
+
DefaultCacheStrategy,
|
|
17787
18505
|
DefaultEntityMaterializer,
|
|
17788
18506
|
DefaultHasManyCollection,
|
|
17789
18507
|
DefaultManyToManyCollection,
|
|
@@ -17798,8 +18516,10 @@ export {
|
|
|
17798
18516
|
InsertQueryBuilder,
|
|
17799
18517
|
IntegerTypeStrategy,
|
|
17800
18518
|
InterceptorPipeline,
|
|
18519
|
+
KeyvCacheAdapter,
|
|
17801
18520
|
Length,
|
|
17802
18521
|
Lower,
|
|
18522
|
+
MemoryCacheAdapter,
|
|
17803
18523
|
MySqlDialect,
|
|
17804
18524
|
NestedSetStrategy,
|
|
17805
18525
|
Orm,
|
|
@@ -17809,12 +18529,14 @@ export {
|
|
|
17809
18529
|
PostgresDialect,
|
|
17810
18530
|
PrimaryKey,
|
|
17811
18531
|
PrototypeMaterializationStrategy,
|
|
18532
|
+
QueryCacheManager,
|
|
17812
18533
|
RelationKinds,
|
|
17813
18534
|
STANDARD_COLUMN_TYPES,
|
|
17814
18535
|
SelectQueryBuilder,
|
|
17815
18536
|
SqlServerDialect,
|
|
17816
18537
|
SqliteDialect,
|
|
17817
18538
|
StringTypeStrategy,
|
|
18539
|
+
TagIndex,
|
|
17818
18540
|
Title,
|
|
17819
18541
|
Tree,
|
|
17820
18542
|
TreeChildren,
|
|
@@ -17934,6 +18656,7 @@ export {
|
|
|
17934
18656
|
extractScopeValues,
|
|
17935
18657
|
firstValue,
|
|
17936
18658
|
floor,
|
|
18659
|
+
formatDuration,
|
|
17937
18660
|
formatTreeList,
|
|
17938
18661
|
fromUnixTime,
|
|
17939
18662
|
generateComponentSchemas,
|
|
@@ -17989,6 +18712,7 @@ export {
|
|
|
17989
18712
|
isOperandNode,
|
|
17990
18713
|
isTableDef2 as isTableDef,
|
|
17991
18714
|
isTreeConfig,
|
|
18715
|
+
isValidDuration,
|
|
17992
18716
|
isValueOperandInput,
|
|
17993
18717
|
isWindowFunctionNode,
|
|
17994
18718
|
jsonArrayAgg,
|
|
@@ -18049,6 +18773,7 @@ export {
|
|
|
18049
18773
|
pagedResponseToOpenApiSchema,
|
|
18050
18774
|
paginationParamsSchema,
|
|
18051
18775
|
parameterToRef,
|
|
18776
|
+
parseDuration,
|
|
18052
18777
|
pi,
|
|
18053
18778
|
pick,
|
|
18054
18779
|
position,
|