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.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) => {
|
|
@@ -6915,10 +6974,28 @@ var setLazyOptionsIfEmpty = (entity, relationName, options) => {
|
|
|
6915
6974
|
if (!meta || meta.lazyRelationOptions.has(relationName)) return;
|
|
6916
6975
|
meta.lazyRelationOptions.set(relationName, options);
|
|
6917
6976
|
};
|
|
6977
|
+
var batchByTable = (pending) => {
|
|
6978
|
+
if (pending.length <= 1) return pending;
|
|
6979
|
+
const byTable = /* @__PURE__ */ new Map();
|
|
6980
|
+
let ungroupedIndex = 0;
|
|
6981
|
+
for (const { entities, include } of pending) {
|
|
6982
|
+
const meta = entities.length ? getEntityMeta(entities[0]) : void 0;
|
|
6983
|
+
const tableKey2 = meta?.table?.name ?? `__ungrouped_${ungroupedIndex++}`;
|
|
6984
|
+
const existing = byTable.get(tableKey2);
|
|
6985
|
+
if (existing) {
|
|
6986
|
+
existing.entities.push(...entities);
|
|
6987
|
+
existing.include = mergeRelationIncludeTrees(existing.include, include);
|
|
6988
|
+
} else {
|
|
6989
|
+
byTable.set(tableKey2, { entities: [...entities], include: { ...include } });
|
|
6990
|
+
}
|
|
6991
|
+
}
|
|
6992
|
+
return Array.from(byTable.values());
|
|
6993
|
+
};
|
|
6918
6994
|
var preloadRelationIncludes = async (entities, includeTree, depth = 0) => {
|
|
6919
6995
|
if (!entities.length) return;
|
|
6920
6996
|
const entries = Object.entries(includeTree);
|
|
6921
6997
|
if (!entries.length) return;
|
|
6998
|
+
const pending = [];
|
|
6922
6999
|
for (const [relationName, node] of entries) {
|
|
6923
7000
|
const shouldLoad = depth > 0 || Boolean(node.include);
|
|
6924
7001
|
if (!shouldLoad) continue;
|
|
@@ -6930,9 +7007,13 @@ var preloadRelationIncludes = async (entities, includeTree, depth = 0) => {
|
|
|
6930
7007
|
);
|
|
6931
7008
|
const relatedEntities = loaded.flat();
|
|
6932
7009
|
if (node.include && relatedEntities.length) {
|
|
6933
|
-
|
|
7010
|
+
pending.push({ entities: relatedEntities, include: node.include });
|
|
6934
7011
|
}
|
|
6935
7012
|
}
|
|
7013
|
+
const batched = batchByTable(pending);
|
|
7014
|
+
for (const { entities: batchEntities, include } of batched) {
|
|
7015
|
+
await preloadRelationIncludes(batchEntities, include, depth + 1);
|
|
7016
|
+
}
|
|
6936
7017
|
};
|
|
6937
7018
|
|
|
6938
7019
|
// src/orm/execute.ts
|
|
@@ -7635,6 +7716,53 @@ var SelectRelationFacet = class {
|
|
|
7635
7716
|
}
|
|
7636
7717
|
};
|
|
7637
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
|
+
|
|
7638
7766
|
// src/query-builder/select.ts
|
|
7639
7767
|
var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
7640
7768
|
env;
|
|
@@ -7651,6 +7779,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7651
7779
|
lazyRelationOptions;
|
|
7652
7780
|
entityConstructor;
|
|
7653
7781
|
includeTree;
|
|
7782
|
+
cacheFacet;
|
|
7783
|
+
cacheContext;
|
|
7654
7784
|
/**
|
|
7655
7785
|
* Creates a new SelectQueryBuilder instance
|
|
7656
7786
|
* @param table - Table definition to query
|
|
@@ -7658,7 +7788,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7658
7788
|
* @param hydration - Optional hydration manager
|
|
7659
7789
|
* @param dependencies - Optional query builder dependencies
|
|
7660
7790
|
*/
|
|
7661
|
-
constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions, entityConstructor, includeTree) {
|
|
7791
|
+
constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions, entityConstructor, includeTree, cacheContext) {
|
|
7662
7792
|
const deps = resolveSelectQueryBuilderDependencies(dependencies);
|
|
7663
7793
|
this.env = { table, deps };
|
|
7664
7794
|
const createAstService = (nextState) => deps.createQueryAstService(table, nextState);
|
|
@@ -7672,6 +7802,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7672
7802
|
this.lazyRelationOptions = new Map(lazyRelationOptions ?? []);
|
|
7673
7803
|
this.entityConstructor = entityConstructor;
|
|
7674
7804
|
this.includeTree = includeTree ?? {};
|
|
7805
|
+
this.cacheFacet = new CacheFacet();
|
|
7806
|
+
this.cacheContext = cacheContext ?? { state: {} };
|
|
7675
7807
|
this.columnSelector = deps.createColumnSelector(this.env);
|
|
7676
7808
|
const relationManager = deps.createRelationManager(this.env);
|
|
7677
7809
|
this.fromFacet = new SelectFromFacet(this.env, createAstService);
|
|
@@ -7688,7 +7820,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7688
7820
|
* @param lazyRelations - Updated lazy relations set
|
|
7689
7821
|
* @returns New SelectQueryBuilder instance
|
|
7690
7822
|
*/
|
|
7691
|
-
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) {
|
|
7692
7824
|
return new _SelectQueryBuilder(
|
|
7693
7825
|
this.env.table,
|
|
7694
7826
|
context.state,
|
|
@@ -7697,7 +7829,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
7697
7829
|
lazyRelations,
|
|
7698
7830
|
lazyRelationOptions,
|
|
7699
7831
|
this.entityConstructor,
|
|
7700
|
-
includeTree
|
|
7832
|
+
includeTree,
|
|
7833
|
+
cacheContext
|
|
7701
7834
|
);
|
|
7702
7835
|
}
|
|
7703
7836
|
/**
|
|
@@ -8119,10 +8252,43 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
8119
8252
|
}
|
|
8120
8253
|
return this;
|
|
8121
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
|
+
}
|
|
8122
8287
|
/**
|
|
8123
8288
|
* Executes the query and returns hydrated results.
|
|
8124
8289
|
* If the builder was created with an entity constructor (e.g. via selectFromEntity),
|
|
8125
8290
|
* this will automatically return fully materialized entity instances.
|
|
8291
|
+
* If caching is configured, results will be cached/retrieved from cache.
|
|
8126
8292
|
*
|
|
8127
8293
|
* @param ctx - ORM session context
|
|
8128
8294
|
* @returns Promise of entity instances (or objects if generic T is not an entity)
|
|
@@ -8132,6 +8298,20 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
8132
8298
|
* users[0] instanceof User; // true
|
|
8133
8299
|
*/
|
|
8134
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) {
|
|
8135
8315
|
if (this.entityConstructor) {
|
|
8136
8316
|
return this.executeAs(this.entityConstructor, ctx);
|
|
8137
8317
|
}
|
|
@@ -12453,12 +12633,13 @@ var UnitOfWork = class {
|
|
|
12453
12633
|
await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
|
|
12454
12634
|
const payload = this.extractColumns(tracked.table, tracked.entity);
|
|
12455
12635
|
let builder = new InsertQueryBuilder(tracked.table).values(payload);
|
|
12456
|
-
if (this.dialect.
|
|
12636
|
+
if (this.dialect.supportsDmlReturningClause()) {
|
|
12457
12637
|
builder = builder.returning(...this.getReturningColumns(tracked.table));
|
|
12458
12638
|
}
|
|
12459
12639
|
const compiled = builder.compile(this.dialect);
|
|
12460
12640
|
const results = await this.executeCompiled(compiled);
|
|
12461
12641
|
this.applyReturningResults(tracked, results);
|
|
12642
|
+
this.applyInsertedIdIfAbsent(tracked, results);
|
|
12462
12643
|
tracked.status = "managed" /* Managed */;
|
|
12463
12644
|
tracked.original = this.createSnapshot(tracked.table, tracked.entity);
|
|
12464
12645
|
tracked.pk = this.getPrimaryKeyValue(tracked);
|
|
@@ -12480,7 +12661,7 @@ var UnitOfWork = class {
|
|
|
12480
12661
|
const pkColumn = tracked.table.columns[findPrimaryKey(tracked.table)];
|
|
12481
12662
|
if (!pkColumn) return;
|
|
12482
12663
|
let builder = new UpdateQueryBuilder(tracked.table).set(changes).where(eq(pkColumn, tracked.pk));
|
|
12483
|
-
if (this.dialect.
|
|
12664
|
+
if (this.dialect.supportsDmlReturningClause()) {
|
|
12484
12665
|
builder = builder.returning(...this.getReturningColumns(tracked.table));
|
|
12485
12666
|
}
|
|
12486
12667
|
const compiled = builder.compile(this.dialect);
|
|
@@ -12574,9 +12755,8 @@ var UnitOfWork = class {
|
|
|
12574
12755
|
* @param results - Query results
|
|
12575
12756
|
*/
|
|
12576
12757
|
applyReturningResults(tracked, results) {
|
|
12577
|
-
if (!this.dialect.supportsReturning()) return;
|
|
12578
12758
|
const first = results[0];
|
|
12579
|
-
if (!first || first.values.length === 0) return;
|
|
12759
|
+
if (!first || first.columns.length === 0 || first.values.length === 0) return;
|
|
12580
12760
|
const row = first.values[0];
|
|
12581
12761
|
for (let i = 0; i < first.columns.length; i++) {
|
|
12582
12762
|
const columnName = this.normalizeColumnName(first.columns[i]);
|
|
@@ -12584,6 +12764,21 @@ var UnitOfWork = class {
|
|
|
12584
12764
|
tracked.entity[columnName] = row[i];
|
|
12585
12765
|
}
|
|
12586
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
|
+
}
|
|
12587
12782
|
/**
|
|
12588
12783
|
* Normalizes a column name by removing quotes and table prefixes.
|
|
12589
12784
|
* @param column - The column name to normalize
|
|
@@ -13221,6 +13416,10 @@ var OrmSession = class {
|
|
|
13221
13416
|
domainEvents;
|
|
13222
13417
|
/** The relation change processor */
|
|
13223
13418
|
relationChanges;
|
|
13419
|
+
/** The cache manager for query caching */
|
|
13420
|
+
cacheManager;
|
|
13421
|
+
/** The tenant ID for multi-tenancy support */
|
|
13422
|
+
tenantId;
|
|
13224
13423
|
interceptors;
|
|
13225
13424
|
saveGraphDefaults;
|
|
13226
13425
|
/**
|
|
@@ -13235,6 +13434,8 @@ var OrmSession = class {
|
|
|
13235
13434
|
this.unitOfWork = new UnitOfWork(this.orm.dialect, this.executor, this.identityMap, () => this);
|
|
13236
13435
|
this.relationChanges = new RelationChangeProcessor(this.unitOfWork, this.orm.dialect, this.executor);
|
|
13237
13436
|
this.domainEvents = new DomainEventBus(opts.domainEventHandlers);
|
|
13437
|
+
this.cacheManager = opts.cacheManager;
|
|
13438
|
+
this.tenantId = opts.tenantId;
|
|
13238
13439
|
}
|
|
13239
13440
|
/**
|
|
13240
13441
|
* Releases resources associated with this session (executor/pool leases) and resets tracking.
|
|
@@ -13609,6 +13810,45 @@ var OrmSession = class {
|
|
|
13609
13810
|
entityContext: this
|
|
13610
13811
|
};
|
|
13611
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
|
+
}
|
|
13612
13852
|
/**
|
|
13613
13853
|
* Merges session defaults with per-call saveGraph options.
|
|
13614
13854
|
* @param options - Per-call saveGraph options
|
|
@@ -13646,6 +13886,309 @@ var InterceptorPipeline = class {
|
|
|
13646
13886
|
}
|
|
13647
13887
|
};
|
|
13648
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
|
+
|
|
13649
14192
|
// src/orm/orm.ts
|
|
13650
14193
|
var Orm = class {
|
|
13651
14194
|
/** The database dialect */
|
|
@@ -13654,6 +14197,8 @@ var Orm = class {
|
|
|
13654
14197
|
interceptors;
|
|
13655
14198
|
/** The naming strategy */
|
|
13656
14199
|
namingStrategy;
|
|
14200
|
+
/** The cache manager (if configured) */
|
|
14201
|
+
cacheManager;
|
|
13657
14202
|
executorFactory;
|
|
13658
14203
|
/**
|
|
13659
14204
|
* Creates a new ORM instance.
|
|
@@ -13664,15 +14209,27 @@ var Orm = class {
|
|
|
13664
14209
|
this.interceptors = opts.interceptors ?? new InterceptorPipeline();
|
|
13665
14210
|
this.namingStrategy = opts.namingStrategy ?? new DefaultNamingStrategy();
|
|
13666
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
|
+
}
|
|
13667
14219
|
}
|
|
13668
14220
|
/**
|
|
13669
14221
|
* Creates a new ORM session.
|
|
13670
|
-
* @param options - Optional session options
|
|
14222
|
+
* @param options - Optional session options (e.g., tenantId for multi-tenancy)
|
|
13671
14223
|
* @returns The ORM session
|
|
13672
14224
|
*/
|
|
13673
|
-
createSession() {
|
|
14225
|
+
createSession(options) {
|
|
13674
14226
|
const executor = this.executorFactory.createExecutor();
|
|
13675
|
-
return new OrmSession({
|
|
14227
|
+
return new OrmSession({
|
|
14228
|
+
orm: this,
|
|
14229
|
+
executor,
|
|
14230
|
+
cacheManager: this.cacheManager,
|
|
14231
|
+
tenantId: options?.tenantId
|
|
14232
|
+
});
|
|
13676
14233
|
}
|
|
13677
14234
|
/**
|
|
13678
14235
|
* Executes a function within a transaction.
|
|
@@ -13683,7 +14240,11 @@ var Orm = class {
|
|
|
13683
14240
|
*/
|
|
13684
14241
|
async transaction(fn8) {
|
|
13685
14242
|
const executor = this.executorFactory.createTransactionalExecutor();
|
|
13686
|
-
const session = new OrmSession({
|
|
14243
|
+
const session = new OrmSession({
|
|
14244
|
+
orm: this,
|
|
14245
|
+
executor,
|
|
14246
|
+
cacheManager: this.cacheManager
|
|
14247
|
+
});
|
|
13687
14248
|
try {
|
|
13688
14249
|
return await session.transaction(() => fn8(session));
|
|
13689
14250
|
} finally {
|
|
@@ -14848,7 +15409,15 @@ function createMysqlExecutor(client) {
|
|
|
14848
15409
|
async executeSql(sql, params) {
|
|
14849
15410
|
const [rows] = await client.query(sql, params);
|
|
14850
15411
|
if (!Array.isArray(rows)) {
|
|
14851
|
-
|
|
15412
|
+
const header = rows;
|
|
15413
|
+
return [{
|
|
15414
|
+
columns: [],
|
|
15415
|
+
values: [],
|
|
15416
|
+
meta: {
|
|
15417
|
+
insertId: header.insertId,
|
|
15418
|
+
rowsAffected: header.affectedRows
|
|
15419
|
+
}
|
|
15420
|
+
}];
|
|
14852
15421
|
}
|
|
14853
15422
|
const result = rowsToQueryResult(
|
|
14854
15423
|
rows
|
|
@@ -18157,6 +18726,162 @@ function queryResultsToRows(results) {
|
|
|
18157
18726
|
}
|
|
18158
18727
|
return rows;
|
|
18159
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
|
+
};
|
|
18160
18885
|
// Annotate the CommonJS export names for ESM import in node:
|
|
18161
18886
|
0 && (module.exports = {
|
|
18162
18887
|
Alphanumeric,
|
|
@@ -18176,6 +18901,7 @@ function queryResultsToRows(results) {
|
|
|
18176
18901
|
DateTimeTypeStrategy,
|
|
18177
18902
|
DecimalTypeStrategy,
|
|
18178
18903
|
DefaultBelongsToReference,
|
|
18904
|
+
DefaultCacheStrategy,
|
|
18179
18905
|
DefaultEntityMaterializer,
|
|
18180
18906
|
DefaultHasManyCollection,
|
|
18181
18907
|
DefaultManyToManyCollection,
|
|
@@ -18190,8 +18916,10 @@ function queryResultsToRows(results) {
|
|
|
18190
18916
|
InsertQueryBuilder,
|
|
18191
18917
|
IntegerTypeStrategy,
|
|
18192
18918
|
InterceptorPipeline,
|
|
18919
|
+
KeyvCacheAdapter,
|
|
18193
18920
|
Length,
|
|
18194
18921
|
Lower,
|
|
18922
|
+
MemoryCacheAdapter,
|
|
18195
18923
|
MySqlDialect,
|
|
18196
18924
|
NestedSetStrategy,
|
|
18197
18925
|
Orm,
|
|
@@ -18201,12 +18929,14 @@ function queryResultsToRows(results) {
|
|
|
18201
18929
|
PostgresDialect,
|
|
18202
18930
|
PrimaryKey,
|
|
18203
18931
|
PrototypeMaterializationStrategy,
|
|
18932
|
+
QueryCacheManager,
|
|
18204
18933
|
RelationKinds,
|
|
18205
18934
|
STANDARD_COLUMN_TYPES,
|
|
18206
18935
|
SelectQueryBuilder,
|
|
18207
18936
|
SqlServerDialect,
|
|
18208
18937
|
SqliteDialect,
|
|
18209
18938
|
StringTypeStrategy,
|
|
18939
|
+
TagIndex,
|
|
18210
18940
|
Title,
|
|
18211
18941
|
Tree,
|
|
18212
18942
|
TreeChildren,
|
|
@@ -18326,6 +19056,7 @@ function queryResultsToRows(results) {
|
|
|
18326
19056
|
extractScopeValues,
|
|
18327
19057
|
firstValue,
|
|
18328
19058
|
floor,
|
|
19059
|
+
formatDuration,
|
|
18329
19060
|
formatTreeList,
|
|
18330
19061
|
fromUnixTime,
|
|
18331
19062
|
generateComponentSchemas,
|
|
@@ -18381,6 +19112,7 @@ function queryResultsToRows(results) {
|
|
|
18381
19112
|
isOperandNode,
|
|
18382
19113
|
isTableDef,
|
|
18383
19114
|
isTreeConfig,
|
|
19115
|
+
isValidDuration,
|
|
18384
19116
|
isValueOperandInput,
|
|
18385
19117
|
isWindowFunctionNode,
|
|
18386
19118
|
jsonArrayAgg,
|
|
@@ -18441,6 +19173,7 @@ function queryResultsToRows(results) {
|
|
|
18441
19173
|
pagedResponseToOpenApiSchema,
|
|
18442
19174
|
paginationParamsSchema,
|
|
18443
19175
|
parameterToRef,
|
|
19176
|
+
parseDuration,
|
|
18444
19177
|
pi,
|
|
18445
19178
|
pick,
|
|
18446
19179
|
position,
|