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.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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
3194
|
-
return `DELETE ${this.quoteIdentifier(alias)} FROM ${target}${joins}${whereClause}
|
|
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.
|
|
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.
|
|
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({
|
|
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({
|
|
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
|
-
|
|
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,
|