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/README.md +26 -14
- package/dist/index.cjs +728 -17
- 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 +720 -17
- 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/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) => {
|
|
@@ -7266,6 +7317,53 @@ var SelectRelationFacet = class {
|
|
|
7266
7317
|
}
|
|
7267
7318
|
};
|
|
7268
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
|
+
|
|
7269
7367
|
// src/query-builder/select.ts
|
|
7270
7368
|
var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
7271
7369
|
env;
|
|
@@ -7282,6 +7380,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7282
7380
|
lazyRelationOptions;
|
|
7283
7381
|
entityConstructor;
|
|
7284
7382
|
includeTree;
|
|
7383
|
+
cacheFacet;
|
|
7384
|
+
cacheContext;
|
|
7285
7385
|
/**
|
|
7286
7386
|
* Creates a new SelectQueryBuilder instance
|
|
7287
7387
|
* @param table - Table definition to query
|
|
@@ -7289,7 +7389,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7289
7389
|
* @param hydration - Optional hydration manager
|
|
7290
7390
|
* @param dependencies - Optional query builder dependencies
|
|
7291
7391
|
*/
|
|
7292
|
-
constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions, entityConstructor, includeTree) {
|
|
7392
|
+
constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions, entityConstructor, includeTree, cacheContext) {
|
|
7293
7393
|
const deps = resolveSelectQueryBuilderDependencies(dependencies);
|
|
7294
7394
|
this.env = { table, deps };
|
|
7295
7395
|
const createAstService = (nextState) => deps.createQueryAstService(table, nextState);
|
|
@@ -7303,6 +7403,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7303
7403
|
this.lazyRelationOptions = new Map(lazyRelationOptions ?? []);
|
|
7304
7404
|
this.entityConstructor = entityConstructor;
|
|
7305
7405
|
this.includeTree = includeTree ?? {};
|
|
7406
|
+
this.cacheFacet = new CacheFacet();
|
|
7407
|
+
this.cacheContext = cacheContext ?? { state: {} };
|
|
7306
7408
|
this.columnSelector = deps.createColumnSelector(this.env);
|
|
7307
7409
|
const relationManager = deps.createRelationManager(this.env);
|
|
7308
7410
|
this.fromFacet = new SelectFromFacet(this.env, createAstService);
|
|
@@ -7319,7 +7421,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7319
7421
|
* @param lazyRelations - Updated lazy relations set
|
|
7320
7422
|
* @returns New SelectQueryBuilder instance
|
|
7321
7423
|
*/
|
|
7322
|
-
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) {
|
|
7323
7425
|
return new _SelectQueryBuilder(
|
|
7324
7426
|
this.env.table,
|
|
7325
7427
|
context.state,
|
|
@@ -7328,7 +7430,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7328
7430
|
lazyRelations,
|
|
7329
7431
|
lazyRelationOptions,
|
|
7330
7432
|
this.entityConstructor,
|
|
7331
|
-
includeTree
|
|
7433
|
+
includeTree,
|
|
7434
|
+
cacheContext
|
|
7332
7435
|
);
|
|
7333
7436
|
}
|
|
7334
7437
|
/**
|
|
@@ -7750,10 +7853,43 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7750
7853
|
}
|
|
7751
7854
|
return this;
|
|
7752
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
|
+
}
|
|
7753
7888
|
/**
|
|
7754
7889
|
* Executes the query and returns hydrated results.
|
|
7755
7890
|
* If the builder was created with an entity constructor (e.g. via selectFromEntity),
|
|
7756
7891
|
* this will automatically return fully materialized entity instances.
|
|
7892
|
+
* If caching is configured, results will be cached/retrieved from cache.
|
|
7757
7893
|
*
|
|
7758
7894
|
* @param ctx - ORM session context
|
|
7759
7895
|
* @returns Promise of entity instances (or objects if generic T is not an entity)
|
|
@@ -7763,6 +7899,20 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7763
7899
|
* users[0] instanceof User; // true
|
|
7764
7900
|
*/
|
|
7765
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) {
|
|
7766
7916
|
if (this.entityConstructor) {
|
|
7767
7917
|
return this.executeAs(this.entityConstructor, ctx);
|
|
7768
7918
|
}
|
|
@@ -12084,12 +12234,13 @@ var UnitOfWork = class {
|
|
|
12084
12234
|
await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
|
|
12085
12235
|
const payload = this.extractColumns(tracked.table, tracked.entity);
|
|
12086
12236
|
let builder = new InsertQueryBuilder(tracked.table).values(payload);
|
|
12087
|
-
if (this.dialect.
|
|
12237
|
+
if (this.dialect.supportsDmlReturningClause()) {
|
|
12088
12238
|
builder = builder.returning(...this.getReturningColumns(tracked.table));
|
|
12089
12239
|
}
|
|
12090
12240
|
const compiled = builder.compile(this.dialect);
|
|
12091
12241
|
const results = await this.executeCompiled(compiled);
|
|
12092
12242
|
this.applyReturningResults(tracked, results);
|
|
12243
|
+
this.applyInsertedIdIfAbsent(tracked, results);
|
|
12093
12244
|
tracked.status = "managed" /* Managed */;
|
|
12094
12245
|
tracked.original = this.createSnapshot(tracked.table, tracked.entity);
|
|
12095
12246
|
tracked.pk = this.getPrimaryKeyValue(tracked);
|
|
@@ -12111,7 +12262,7 @@ var UnitOfWork = class {
|
|
|
12111
12262
|
const pkColumn = tracked.table.columns[findPrimaryKey(tracked.table)];
|
|
12112
12263
|
if (!pkColumn) return;
|
|
12113
12264
|
let builder = new UpdateQueryBuilder(tracked.table).set(changes).where(eq(pkColumn, tracked.pk));
|
|
12114
|
-
if (this.dialect.
|
|
12265
|
+
if (this.dialect.supportsDmlReturningClause()) {
|
|
12115
12266
|
builder = builder.returning(...this.getReturningColumns(tracked.table));
|
|
12116
12267
|
}
|
|
12117
12268
|
const compiled = builder.compile(this.dialect);
|
|
@@ -12205,9 +12356,8 @@ var UnitOfWork = class {
|
|
|
12205
12356
|
* @param results - Query results
|
|
12206
12357
|
*/
|
|
12207
12358
|
applyReturningResults(tracked, results) {
|
|
12208
|
-
if (!this.dialect.supportsReturning()) return;
|
|
12209
12359
|
const first = results[0];
|
|
12210
|
-
if (!first || first.values.length === 0) return;
|
|
12360
|
+
if (!first || first.columns.length === 0 || first.values.length === 0) return;
|
|
12211
12361
|
const row = first.values[0];
|
|
12212
12362
|
for (let i = 0; i < first.columns.length; i++) {
|
|
12213
12363
|
const columnName = this.normalizeColumnName(first.columns[i]);
|
|
@@ -12215,6 +12365,21 @@ var UnitOfWork = class {
|
|
|
12215
12365
|
tracked.entity[columnName] = row[i];
|
|
12216
12366
|
}
|
|
12217
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
|
+
}
|
|
12218
12383
|
/**
|
|
12219
12384
|
* Normalizes a column name by removing quotes and table prefixes.
|
|
12220
12385
|
* @param column - The column name to normalize
|
|
@@ -12852,6 +13017,10 @@ var OrmSession = class {
|
|
|
12852
13017
|
domainEvents;
|
|
12853
13018
|
/** The relation change processor */
|
|
12854
13019
|
relationChanges;
|
|
13020
|
+
/** The cache manager for query caching */
|
|
13021
|
+
cacheManager;
|
|
13022
|
+
/** The tenant ID for multi-tenancy support */
|
|
13023
|
+
tenantId;
|
|
12855
13024
|
interceptors;
|
|
12856
13025
|
saveGraphDefaults;
|
|
12857
13026
|
/**
|
|
@@ -12866,6 +13035,8 @@ var OrmSession = class {
|
|
|
12866
13035
|
this.unitOfWork = new UnitOfWork(this.orm.dialect, this.executor, this.identityMap, () => this);
|
|
12867
13036
|
this.relationChanges = new RelationChangeProcessor(this.unitOfWork, this.orm.dialect, this.executor);
|
|
12868
13037
|
this.domainEvents = new DomainEventBus(opts.domainEventHandlers);
|
|
13038
|
+
this.cacheManager = opts.cacheManager;
|
|
13039
|
+
this.tenantId = opts.tenantId;
|
|
12869
13040
|
}
|
|
12870
13041
|
/**
|
|
12871
13042
|
* Releases resources associated with this session (executor/pool leases) and resets tracking.
|
|
@@ -13240,6 +13411,45 @@ var OrmSession = class {
|
|
|
13240
13411
|
entityContext: this
|
|
13241
13412
|
};
|
|
13242
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
|
+
}
|
|
13243
13453
|
/**
|
|
13244
13454
|
* Merges session defaults with per-call saveGraph options.
|
|
13245
13455
|
* @param options - Per-call saveGraph options
|
|
@@ -13277,6 +13487,309 @@ var InterceptorPipeline = class {
|
|
|
13277
13487
|
}
|
|
13278
13488
|
};
|
|
13279
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
|
+
|
|
13280
13793
|
// src/orm/orm.ts
|
|
13281
13794
|
var Orm = class {
|
|
13282
13795
|
/** The database dialect */
|
|
@@ -13285,6 +13798,8 @@ var Orm = class {
|
|
|
13285
13798
|
interceptors;
|
|
13286
13799
|
/** The naming strategy */
|
|
13287
13800
|
namingStrategy;
|
|
13801
|
+
/** The cache manager (if configured) */
|
|
13802
|
+
cacheManager;
|
|
13288
13803
|
executorFactory;
|
|
13289
13804
|
/**
|
|
13290
13805
|
* Creates a new ORM instance.
|
|
@@ -13295,15 +13810,27 @@ var Orm = class {
|
|
|
13295
13810
|
this.interceptors = opts.interceptors ?? new InterceptorPipeline();
|
|
13296
13811
|
this.namingStrategy = opts.namingStrategy ?? new DefaultNamingStrategy();
|
|
13297
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
|
+
}
|
|
13298
13820
|
}
|
|
13299
13821
|
/**
|
|
13300
13822
|
* Creates a new ORM session.
|
|
13301
|
-
* @param options - Optional session options
|
|
13823
|
+
* @param options - Optional session options (e.g., tenantId for multi-tenancy)
|
|
13302
13824
|
* @returns The ORM session
|
|
13303
13825
|
*/
|
|
13304
|
-
createSession() {
|
|
13826
|
+
createSession(options) {
|
|
13305
13827
|
const executor = this.executorFactory.createExecutor();
|
|
13306
|
-
return new OrmSession({
|
|
13828
|
+
return new OrmSession({
|
|
13829
|
+
orm: this,
|
|
13830
|
+
executor,
|
|
13831
|
+
cacheManager: this.cacheManager,
|
|
13832
|
+
tenantId: options?.tenantId
|
|
13833
|
+
});
|
|
13307
13834
|
}
|
|
13308
13835
|
/**
|
|
13309
13836
|
* Executes a function within a transaction.
|
|
@@ -13314,7 +13841,11 @@ var Orm = class {
|
|
|
13314
13841
|
*/
|
|
13315
13842
|
async transaction(fn8) {
|
|
13316
13843
|
const executor = this.executorFactory.createTransactionalExecutor();
|
|
13317
|
-
const session = new OrmSession({
|
|
13844
|
+
const session = new OrmSession({
|
|
13845
|
+
orm: this,
|
|
13846
|
+
executor,
|
|
13847
|
+
cacheManager: this.cacheManager
|
|
13848
|
+
});
|
|
13318
13849
|
try {
|
|
13319
13850
|
return await session.transaction(() => fn8(session));
|
|
13320
13851
|
} finally {
|
|
@@ -14479,7 +15010,15 @@ function createMysqlExecutor(client) {
|
|
|
14479
15010
|
async executeSql(sql, params) {
|
|
14480
15011
|
const [rows] = await client.query(sql, params);
|
|
14481
15012
|
if (!Array.isArray(rows)) {
|
|
14482
|
-
|
|
15013
|
+
const header = rows;
|
|
15014
|
+
return [{
|
|
15015
|
+
columns: [],
|
|
15016
|
+
values: [],
|
|
15017
|
+
meta: {
|
|
15018
|
+
insertId: header.insertId,
|
|
15019
|
+
rowsAffected: header.affectedRows
|
|
15020
|
+
}
|
|
15021
|
+
}];
|
|
14483
15022
|
}
|
|
14484
15023
|
const result = rowsToQueryResult(
|
|
14485
15024
|
rows
|
|
@@ -17788,6 +18327,162 @@ function queryResultsToRows(results) {
|
|
|
17788
18327
|
}
|
|
17789
18328
|
return rows;
|
|
17790
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
|
+
};
|
|
17791
18486
|
export {
|
|
17792
18487
|
Alphanumeric,
|
|
17793
18488
|
AsyncLocalStorage,
|
|
@@ -17806,6 +18501,7 @@ export {
|
|
|
17806
18501
|
DateTimeTypeStrategy,
|
|
17807
18502
|
DecimalTypeStrategy,
|
|
17808
18503
|
DefaultBelongsToReference,
|
|
18504
|
+
DefaultCacheStrategy,
|
|
17809
18505
|
DefaultEntityMaterializer,
|
|
17810
18506
|
DefaultHasManyCollection,
|
|
17811
18507
|
DefaultManyToManyCollection,
|
|
@@ -17820,8 +18516,10 @@ export {
|
|
|
17820
18516
|
InsertQueryBuilder,
|
|
17821
18517
|
IntegerTypeStrategy,
|
|
17822
18518
|
InterceptorPipeline,
|
|
18519
|
+
KeyvCacheAdapter,
|
|
17823
18520
|
Length,
|
|
17824
18521
|
Lower,
|
|
18522
|
+
MemoryCacheAdapter,
|
|
17825
18523
|
MySqlDialect,
|
|
17826
18524
|
NestedSetStrategy,
|
|
17827
18525
|
Orm,
|
|
@@ -17831,12 +18529,14 @@ export {
|
|
|
17831
18529
|
PostgresDialect,
|
|
17832
18530
|
PrimaryKey,
|
|
17833
18531
|
PrototypeMaterializationStrategy,
|
|
18532
|
+
QueryCacheManager,
|
|
17834
18533
|
RelationKinds,
|
|
17835
18534
|
STANDARD_COLUMN_TYPES,
|
|
17836
18535
|
SelectQueryBuilder,
|
|
17837
18536
|
SqlServerDialect,
|
|
17838
18537
|
SqliteDialect,
|
|
17839
18538
|
StringTypeStrategy,
|
|
18539
|
+
TagIndex,
|
|
17840
18540
|
Title,
|
|
17841
18541
|
Tree,
|
|
17842
18542
|
TreeChildren,
|
|
@@ -17956,6 +18656,7 @@ export {
|
|
|
17956
18656
|
extractScopeValues,
|
|
17957
18657
|
firstValue,
|
|
17958
18658
|
floor,
|
|
18659
|
+
formatDuration,
|
|
17959
18660
|
formatTreeList,
|
|
17960
18661
|
fromUnixTime,
|
|
17961
18662
|
generateComponentSchemas,
|
|
@@ -18011,6 +18712,7 @@ export {
|
|
|
18011
18712
|
isOperandNode,
|
|
18012
18713
|
isTableDef2 as isTableDef,
|
|
18013
18714
|
isTreeConfig,
|
|
18715
|
+
isValidDuration,
|
|
18014
18716
|
isValueOperandInput,
|
|
18015
18717
|
isWindowFunctionNode,
|
|
18016
18718
|
jsonArrayAgg,
|
|
@@ -18071,6 +18773,7 @@ export {
|
|
|
18071
18773
|
pagedResponseToOpenApiSchema,
|
|
18072
18774
|
paginationParamsSchema,
|
|
18073
18775
|
parameterToRef,
|
|
18776
|
+
parseDuration,
|
|
18074
18777
|
pi,
|
|
18075
18778
|
pick,
|
|
18076
18779
|
position,
|