ts-prorm-orm 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/CHANGELOG.md +1111 -0
- package/LICENSE +21 -0
- package/README.md +573 -0
- package/dist/audit/history-query.js +180 -0
- package/dist/audit/index.js +23 -0
- package/dist/audit/logger.js +236 -0
- package/dist/cache/cache-manager.js +84 -0
- package/dist/cache/index.js +16 -0
- package/dist/cache/redis-cluster-cache.js +557 -0
- package/dist/cli.js +2200 -0
- package/dist/compliance/audit-trail.js +68 -0
- package/dist/compliance/backup-verification.js +685 -0
- package/dist/compliance/breach-detector.js +505 -0
- package/dist/compliance/consent-record.js +227 -0
- package/dist/compliance/consent-versioning.js +331 -0
- package/dist/compliance/cross-border-log.js +530 -0
- package/dist/compliance/data-classifier.js +258 -0
- package/dist/compliance/data-lineage.js +565 -0
- package/dist/compliance/data-masker.js +303 -0
- package/dist/compliance/data-portability.js +265 -0
- package/dist/compliance/data-retention.js +195 -0
- package/dist/compliance/dsar-workflow.js +342 -0
- package/dist/compliance/field-encryption.js +223 -0
- package/dist/compliance/immutable-record.js +148 -0
- package/dist/compliance/index.js +260 -0
- package/dist/compliance/privacy-impact-assessment.js +364 -0
- package/dist/compliance/pseudonymization.js +269 -0
- package/dist/compliance/query-firewall.js +1182 -0
- package/dist/compliance/rate-limiter.js +580 -0
- package/dist/compliance/right-to-erasure.js +117 -0
- package/dist/compliance/row-level-security.js +246 -0
- package/dist/compliance/security-decorator.js +574 -0
- package/dist/compliance/security-monitor.js +525 -0
- package/dist/compliance/sensitive-data-discovery.js +476 -0
- package/dist/compliance/session-isolation.js +472 -0
- package/dist/compliance/tls-enforcer.js +108 -0
- package/dist/compliance/worm-storage.js +712 -0
- package/dist/connection-manager.js +198 -0
- package/dist/connection-pool.js +519 -0
- package/dist/decorators/audit.js +136 -0
- package/dist/decorators/belongs-to-many.js +115 -0
- package/dist/decorators/belongs-to.js +115 -0
- package/dist/decorators/check.js +435 -0
- package/dist/decorators/collate.js +329 -0
- package/dist/decorators/comment.js +205 -0
- package/dist/decorators/database-settings.js +236 -0
- package/dist/decorators/default.js +244 -0
- package/dist/decorators/encryption.js +235 -0
- package/dist/decorators/engine.js +97 -0
- package/dist/decorators/fk-constraints.js +594 -0
- package/dist/decorators/foreign-table.js +136 -0
- package/dist/decorators/generated.js +274 -0
- package/dist/decorators/has-many.js +127 -0
- package/dist/decorators/has-one.js +116 -0
- package/dist/decorators/hstore.js +129 -0
- package/dist/decorators/index.js +355 -0
- package/dist/decorators/json-column.js +83 -0
- package/dist/decorators/jsonb.js +101 -0
- package/dist/decorators/orm-decorators.js +425 -0
- package/dist/decorators/permissions.js +294 -0
- package/dist/decorators/procedure.js +210 -0
- package/dist/decorators/query-options.js +556 -0
- package/dist/decorators/range.js +167 -0
- package/dist/decorators/set-column.js +83 -0
- package/dist/decorators/spatial.js +114 -0
- package/dist/decorators/storage.js +580 -0
- package/dist/decorators/timezone.js +512 -0
- package/dist/decorators/trigger.js +90 -0
- package/dist/decorators/uuid.js +135 -0
- package/dist/decorators/view.js +258 -0
- package/dist/diagrams/chen-diagram.js +354 -0
- package/dist/diagrams/class-diagram.js +384 -0
- package/dist/diagrams/dependency-diagram.js +432 -0
- package/dist/diagrams/er-diagram.js +605 -0
- package/dist/diagrams/flow-diagram.js +394 -0
- package/dist/diagrams/gantt-diagram.js +411 -0
- package/dist/diagrams/index-diagram.js +353 -0
- package/dist/diagrams/index.js +184 -0
- package/dist/diagrams/migration-diagram.js +316 -0
- package/dist/diagrams/model-diagram.js +616 -0
- package/dist/diagrams/package-diagram.js +376 -0
- package/dist/diagrams/palette.js +85 -0
- package/dist/diagrams/relational-diagram.js +455 -0
- package/dist/diagrams/schemadoc-diagram.js +309 -0
- package/dist/diagrams/sequence-diagram.js +307 -0
- package/dist/diagrams/state-diagram.js +344 -0
- package/dist/diagrams/svg-dom.js +111 -0
- package/dist/diagrams/tree-diagram.js +250 -0
- package/dist/dialects/clickhouse/index.js +1742 -0
- package/dist/dialects/cockroachdb/index.js +4677 -0
- package/dist/dialects/cratedb/index.js +441 -0
- package/dist/dialects/databricks/index.js +517 -0
- package/dist/dialects/db2/index.js +2225 -0
- package/dist/dialects/dialect.js +720 -0
- package/dist/dialects/duckdb/index.js +2014 -0
- package/dist/dialects/exasol/index.js +357 -0
- package/dist/dialects/firebird/index.js +495 -0
- package/dist/dialects/greenplum/index.js +457 -0
- package/dist/dialects/hana/index.js +1741 -0
- package/dist/dialects/mariadb/index.js +3590 -0
- package/dist/dialects/mssql/index.js +2296 -0
- package/dist/dialects/mysql/index.js +4058 -0
- package/dist/dialects/oracle/index.js +2999 -0
- package/dist/dialects/postgres/index.js +5319 -0
- package/dist/dialects/query-stream-helper.js +152 -0
- package/dist/dialects/questdb/index.js +433 -0
- package/dist/dialects/redshift/index.js +2277 -0
- package/dist/dialects/singlestore/index.js +354 -0
- package/dist/dialects/snowflake/index.js +2082 -0
- package/dist/dialects/spanner/index.js +1768 -0
- package/dist/dialects/sqlite/index.js +3382 -0
- package/dist/dialects/tidb/index.js +377 -0
- package/dist/dialects/timescaledb/index.js +164 -0
- package/dist/dialects/trino/index.js +420 -0
- package/dist/dialects/turso/index.js +227 -0
- package/dist/dialects/vertica/index.js +317 -0
- package/dist/dialects/yugabytedb/index.js +424 -0
- package/dist/errors/index.js +472 -0
- package/dist/errors/utils.js +340 -0
- package/dist/errors.js +21 -0
- package/dist/extensions/catalog/cloud-warehouse-features.js +207 -0
- package/dist/extensions/catalog/mssql-features.js +147 -0
- package/dist/extensions/catalog/mysql-mariadb-plugins.js +229 -0
- package/dist/extensions/catalog/oracle-db2-features.js +196 -0
- package/dist/extensions/catalog/postgres-extensions.js +516 -0
- package/dist/extensions/index.js +71 -0
- package/dist/extensions/types.js +13 -0
- package/dist/foreign-data.js +173 -0
- package/dist/hooks/hooks-manager.js +350 -0
- package/dist/hooks/index.js +37 -0
- package/dist/index.js +463 -0
- package/dist/logging.js +335 -0
- package/dist/migrations/index.js +40 -0
- package/dist/migrations/migration.js +196 -0
- package/dist/migrations/migrator.js +411 -0
- package/dist/migrations/prormmigration.js +275 -0
- package/dist/migrations/query-interface.js +435 -0
- package/dist/migrations/seeder.js +353 -0
- package/dist/models/associations.js +852 -0
- package/dist/models/constraints.js +288 -0
- package/dist/models/data-types.js +2518 -0
- package/dist/models/decorators.js +445 -0
- package/dist/models/index.js +33 -0
- package/dist/models/indexes.js +531 -0
- package/dist/models/methods.js +382 -0
- package/dist/models/model-manager.js +103 -0
- package/dist/models/model.js +5349 -0
- package/dist/models/operators.js +67 -0
- package/dist/models/scopes.js +189 -0
- package/dist/models/typescript-types.js +26 -0
- package/dist/nosql/aerospike/index.js +205 -0
- package/dist/nosql/allegrograph/index.js +169 -0
- package/dist/nosql/arangodb/index.js +364 -0
- package/dist/nosql/azure-blob/index.js +206 -0
- package/dist/nosql/beanstalkd/index.js +231 -0
- package/dist/nosql/beequeue/index.js +210 -0
- package/dist/nosql/bigchaindb/index.js +195 -0
- package/dist/nosql/bigtable/index.js +224 -0
- package/dist/nosql/blazegraph/index.js +173 -0
- package/dist/nosql/bullmq/index.js +191 -0
- package/dist/nosql/cassandra/index.js +174 -0
- package/dist/nosql/chroma/index.js +190 -0
- package/dist/nosql/cloudflare-kv/index.js +220 -0
- package/dist/nosql/coherence/index.js +200 -0
- package/dist/nosql/cosmosdb/index.js +157 -0
- package/dist/nosql/couchbase/index.js +213 -0
- package/dist/nosql/dax/index.js +212 -0
- package/dist/nosql/deno-kv/index.js +206 -0
- package/dist/nosql/dgraph/index.js +171 -0
- package/dist/nosql/doris/index.js +169 -0
- package/dist/nosql/druid/index.js +162 -0
- package/dist/nosql/dynamodb/index.js +937 -0
- package/dist/nosql/elasticsearch/index.js +377 -0
- package/dist/nosql/etcd/index.js +502 -0
- package/dist/nosql/eventhubs/index.js +213 -0
- package/dist/nosql/eventstore/index.js +254 -0
- package/dist/nosql/faunadb/index.js +188 -0
- package/dist/nosql/firestore/index.js +177 -0
- package/dist/nosql/fluree/index.js +148 -0
- package/dist/nosql/fuseki/index.js +170 -0
- package/dist/nosql/gcs/index.js +172 -0
- package/dist/nosql/gearman/index.js +160 -0
- package/dist/nosql/geode/index.js +196 -0
- package/dist/nosql/graphdb/index.js +169 -0
- package/dist/nosql/graylog/index.js +188 -0
- package/dist/nosql/gridgain/index.js +171 -0
- package/dist/nosql/hazelcast/index.js +162 -0
- package/dist/nosql/hbase/index.js +230 -0
- package/dist/nosql/ignite/index.js +173 -0
- package/dist/nosql/immudb/index.js +184 -0
- package/dist/nosql/index.js +232 -0
- package/dist/nosql/infinispan/index.js +200 -0
- package/dist/nosql/influxdb/index.js +0 -0
- package/dist/nosql/kafka/index.js +234 -0
- package/dist/nosql/keyspaces/index.js +189 -0
- package/dist/nosql/kinesis/index.js +253 -0
- package/dist/nosql/leveldb/index.js +153 -0
- package/dist/nosql/lmdb/index.js +160 -0
- package/dist/nosql/loki/index.js +201 -0
- package/dist/nosql/marklogic/index.js +204 -0
- package/dist/nosql/materialize/index.js +145 -0
- package/dist/nosql/meilisearch/index.js +154 -0
- package/dist/nosql/memcached/index.js +223 -0
- package/dist/nosql/milvus/index.js +410 -0
- package/dist/nosql/minio/index.js +265 -0
- package/dist/nosql/momento/index.js +179 -0
- package/dist/nosql/mongodb/index.js +461 -0
- package/dist/nosql/nats/index.js +247 -0
- package/dist/nosql/nedb/index.js +164 -0
- package/dist/nosql/neo4j/index.js +450 -0
- package/dist/nosql/neptune/index.js +470 -0
- package/dist/nosql/nsq/index.js +200 -0
- package/dist/nosql/opensearch/index.js +186 -0
- package/dist/nosql/orientdb/index.js +175 -0
- package/dist/nosql/papertrail/index.js +200 -0
- package/dist/nosql/pinecone/index.js +0 -0
- package/dist/nosql/pinot/index.js +133 -0
- package/dist/nosql/pouchdb/index.js +172 -0
- package/dist/nosql/prometheus/index.js +174 -0
- package/dist/nosql/provendb/index.js +147 -0
- package/dist/nosql/pubsub/index.js +187 -0
- package/dist/nosql/pulsar/index.js +232 -0
- package/dist/nosql/qdrant/index.js +295 -0
- package/dist/nosql/qldb/index.js +192 -0
- package/dist/nosql/r2/index.js +281 -0
- package/dist/nosql/rabbitmq/index.js +237 -0
- package/dist/nosql/ravendb/index.js +175 -0
- package/dist/nosql/redis/index.js +607 -0
- package/dist/nosql/redpanda/index.js +237 -0
- package/dist/nosql/resque/index.js +203 -0
- package/dist/nosql/rethinkdb/index.js +232 -0
- package/dist/nosql/rocksdb/index.js +152 -0
- package/dist/nosql/rockset/index.js +126 -0
- package/dist/nosql/s3/index.js +298 -0
- package/dist/nosql/scylladb/index.js +178 -0
- package/dist/nosql/signoz/index.js +227 -0
- package/dist/nosql/sns/index.js +201 -0
- package/dist/nosql/solr/index.js +231 -0
- package/dist/nosql/splunk/index.js +227 -0
- package/dist/nosql/sqs/index.js +246 -0
- package/dist/nosql/stardog/index.js +169 -0
- package/dist/nosql/starrocks/index.js +170 -0
- package/dist/nosql/store.js +2 -0
- package/dist/nosql/sumologic/index.js +213 -0
- package/dist/nosql/surrealdb/index.js +179 -0
- package/dist/nosql/terminusdb/index.js +151 -0
- package/dist/nosql/tigergraph/index.js +469 -0
- package/dist/nosql/typesense/index.js +148 -0
- package/dist/nosql/unqlite/index.js +157 -0
- package/dist/nosql/upstash/index.js +167 -0
- package/dist/nosql/vercel-kv/index.js +166 -0
- package/dist/nosql/victoriametrics/index.js +213 -0
- package/dist/nosql/virtuoso/index.js +169 -0
- package/dist/nosql/weaviate/index.js +276 -0
- package/dist/operators/index.js +108 -0
- package/dist/operators.js +2690 -0
- package/dist/prisma-migrate/index.js +38 -0
- package/dist/prisma-migrate/migration-generator.js +125 -0
- package/dist/prisma-migrate/model-generator.js +172 -0
- package/dist/prisma-migrate/relations.js +100 -0
- package/dist/prisma-migrate/schema-parser.js +167 -0
- package/dist/prisma-migrate/type-mapper.js +40 -0
- package/dist/prorm.js +6832 -0
- package/dist/query-builders/cte-builder.js +80 -0
- package/dist/query-builders/functions/aggregate.js +390 -0
- package/dist/query-builders/functions/conditional.js +503 -0
- package/dist/query-builders/functions/datetime.js +695 -0
- package/dist/query-builders/functions/fulltext.js +439 -0
- package/dist/query-builders/functions/index.js +93 -0
- package/dist/query-builders/functions/json.js +427 -0
- package/dist/query-builders/functions/math.js +399 -0
- package/dist/query-builders/functions/string.js +518 -0
- package/dist/query-builders/functions/window.js +328 -0
- package/dist/query-builders/include-builder.js +323 -0
- package/dist/query-builders/index-expression-builder.js +242 -0
- package/dist/query-builders/index.js +161 -0
- package/dist/query-builders/insert-builder.js +164 -0
- package/dist/query-builders/model-helpers.js +38 -0
- package/dist/query-builders/order-limit-builder.js +239 -0
- package/dist/query-builders/sql-compiler.js +2040 -0
- package/dist/query-builders/subquery-builder.js +152 -0
- package/dist/query-builders/update-builder.js +182 -0
- package/dist/query-builders/view-builder.js +218 -0
- package/dist/query-builders/where-builder.js +1532 -0
- package/dist/query-interface.js +622 -0
- package/dist/query-optimizers/batch-optimizer.js +426 -0
- package/dist/query-optimizers/explain-plans.js +415 -0
- package/dist/query-optimizers/index.js +51 -0
- package/dist/query-optimizers/prepared-statement-cache.js +423 -0
- package/dist/query-optimizers/query-hints.js +438 -0
- package/dist/query-optimizers/query-optimizer.js +278 -0
- package/dist/query-optimizers/slow-query-logger.js +271 -0
- package/dist/replica-manager.js +507 -0
- package/dist/schema/index.js +15 -0
- package/dist/schema/migration-generator.js +374 -0
- package/dist/schema/schema-differ.js +549 -0
- package/dist/schema/types.js +6 -0
- package/dist/sql-constants.js +300 -0
- package/dist/sqlite-advanced.js +1047 -0
- package/dist/streams/index.js +12 -0
- package/dist/streams/transforms.js +178 -0
- package/dist/transaction.js +456 -0
- package/dist/types/index.js +180 -0
- package/dist/user-management.js +127 -0
- package/dist/utils/date.js +300 -0
- package/dist/utils/index.js +1079 -0
- package/dist/utils/string.js +161 -0
- package/dist/validators/index.js +19 -0
- package/dist/validators/validator.js +911 -0
- package/package.json +190 -0
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Redis Cluster Cache Backend
|
|
4
|
+
*
|
|
5
|
+
* Implements CacheBackend using ioredis cluster with L1 (in-memory) and L2 (Redis cluster) caching.
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.RedisClusterCache = void 0;
|
|
42
|
+
exports.createRedisClusterCache = createRedisClusterCache;
|
|
43
|
+
const ioredis_1 = __importStar(require("ioredis"));
|
|
44
|
+
const cache_manager_1 = require("./cache-manager");
|
|
45
|
+
/**
|
|
46
|
+
* LRU Map implementation for L1 cache
|
|
47
|
+
*/
|
|
48
|
+
class LRUCache {
|
|
49
|
+
constructor(maxSize = 1000, ttl = 60000) {
|
|
50
|
+
this.cache = new Map();
|
|
51
|
+
this.maxSize = maxSize;
|
|
52
|
+
this.ttl = ttl;
|
|
53
|
+
}
|
|
54
|
+
get(key) {
|
|
55
|
+
const value = this.cache.get(key);
|
|
56
|
+
if (value === undefined) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
// Move to end (most recently used)
|
|
60
|
+
this.cache.delete(key);
|
|
61
|
+
this.cache.set(key, value);
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
set(key, value) {
|
|
65
|
+
// If key exists, delete it first to update order
|
|
66
|
+
if (this.cache.has(key)) {
|
|
67
|
+
this.cache.delete(key);
|
|
68
|
+
}
|
|
69
|
+
// If at capacity, remove oldest (first) entry
|
|
70
|
+
if (this.cache.size >= this.maxSize) {
|
|
71
|
+
const firstKey = this.cache.keys().next().value;
|
|
72
|
+
if (firstKey) {
|
|
73
|
+
this.cache.delete(firstKey);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
this.cache.set(key, value);
|
|
77
|
+
}
|
|
78
|
+
delete(key) {
|
|
79
|
+
return this.cache.delete(key);
|
|
80
|
+
}
|
|
81
|
+
clear() {
|
|
82
|
+
this.cache.clear();
|
|
83
|
+
}
|
|
84
|
+
keys() {
|
|
85
|
+
return Array.from(this.cache.keys());
|
|
86
|
+
}
|
|
87
|
+
size() {
|
|
88
|
+
return this.cache.size;
|
|
89
|
+
}
|
|
90
|
+
has(key) {
|
|
91
|
+
return this.cache.has(key);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Redis Cluster Cache implementation with L1/L2 support
|
|
96
|
+
*/
|
|
97
|
+
class RedisClusterCache extends cache_manager_1.AbstractCacheManager {
|
|
98
|
+
// Use 'options' from parent class, cast for extended options
|
|
99
|
+
get clusterOptions() {
|
|
100
|
+
return this.options;
|
|
101
|
+
}
|
|
102
|
+
constructor(options = {}) {
|
|
103
|
+
super(options);
|
|
104
|
+
this.redisCluster = null;
|
|
105
|
+
this.redisReplicas = [];
|
|
106
|
+
this.connected = false;
|
|
107
|
+
this.l1Cache = new LRUCache(options.l1?.maxEntries ?? 1000, (options.l1?.ttl ?? 60) * 1000);
|
|
108
|
+
this.initializeCluster();
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Initialize Redis cluster connection
|
|
112
|
+
*/
|
|
113
|
+
initializeCluster() {
|
|
114
|
+
const opts = this.clusterOptions;
|
|
115
|
+
if (!opts.clusterNodes || opts.clusterNodes.length === 0) {
|
|
116
|
+
console.warn('RedisClusterCache: No cluster nodes provided, running in L1-only mode');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const clusterNodes = opts.clusterNodes.map(node => ({
|
|
120
|
+
host: node.host,
|
|
121
|
+
port: node.port,
|
|
122
|
+
password: node.password,
|
|
123
|
+
tls: node.tls,
|
|
124
|
+
}));
|
|
125
|
+
const clusterOptions = {
|
|
126
|
+
maxRedirections: 3,
|
|
127
|
+
enableReadyCheck: true,
|
|
128
|
+
enableOfflineQueue: true,
|
|
129
|
+
lazyConnect: false,
|
|
130
|
+
redisOptions: {
|
|
131
|
+
password: this.options.clusterOptions?.password,
|
|
132
|
+
tls: this.options.clusterOptions?.tls ? {} : undefined,
|
|
133
|
+
connectTimeout: this.options.connectTimeout ?? 10000,
|
|
134
|
+
commandTimeout: this.options.commandTimeout ?? 5000,
|
|
135
|
+
db: this.options.db ?? 0,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
// Add retry strategy
|
|
139
|
+
const toClusterRetryStrategy = (fn) => (times) => {
|
|
140
|
+
const result = fn(times);
|
|
141
|
+
return result instanceof Error ? null : result;
|
|
142
|
+
};
|
|
143
|
+
if (this.options.retryStrategy) {
|
|
144
|
+
clusterOptions.clusterRetryStrategy = toClusterRetryStrategy(this.options.retryStrategy);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
clusterOptions.clusterRetryStrategy = (times) => {
|
|
148
|
+
if (times > 10) {
|
|
149
|
+
return null; // Stop retrying
|
|
150
|
+
}
|
|
151
|
+
return Math.min(times * 200, 3000); // Max 3 seconds
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
this.redisCluster = new ioredis_1.Cluster(clusterNodes, clusterOptions);
|
|
155
|
+
this.redisCluster.on('connect', () => {
|
|
156
|
+
this.connected = true;
|
|
157
|
+
console.log('RedisClusterCache: Connected to cluster');
|
|
158
|
+
});
|
|
159
|
+
this.redisCluster.on('error', (err) => {
|
|
160
|
+
console.error('RedisClusterCache: Cluster error', err.message);
|
|
161
|
+
this.connected = false;
|
|
162
|
+
});
|
|
163
|
+
this.redisCluster.on('close', () => {
|
|
164
|
+
this.connected = false;
|
|
165
|
+
});
|
|
166
|
+
// Initialize read replicas if enabled
|
|
167
|
+
if (this.options.readFromReplicas && this.options.clusterOptions?.enableReadFromReplicas) {
|
|
168
|
+
this.initializeReplicas();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Initialize read replicas for read-from-replicas functionality
|
|
173
|
+
*/
|
|
174
|
+
initializeReplicas() {
|
|
175
|
+
if (!this.options.clusterOptions?.replicaNodes || this.options.clusterOptions.replicaNodes.length === 0) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
for (const node of this.options.clusterOptions.replicaNodes) {
|
|
179
|
+
const redis = new ioredis_1.default({
|
|
180
|
+
host: node.host,
|
|
181
|
+
port: node.port,
|
|
182
|
+
password: node.password,
|
|
183
|
+
tls: node.tls ? {} : undefined,
|
|
184
|
+
db: this.options.db ?? 0,
|
|
185
|
+
connectTimeout: this.options.connectTimeout ?? 10000,
|
|
186
|
+
commandTimeout: this.options.commandTimeout ?? 5000,
|
|
187
|
+
lazyConnect: false,
|
|
188
|
+
});
|
|
189
|
+
redis.on('error', (err) => {
|
|
190
|
+
console.error('RedisClusterCache: Replica error', err.message);
|
|
191
|
+
});
|
|
192
|
+
this.redisReplicas.push(redis);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Get a value from cache, checking L1 first then L2
|
|
197
|
+
*/
|
|
198
|
+
async get(key) {
|
|
199
|
+
const fullKey = this.buildKey(key);
|
|
200
|
+
// Try L1 cache first
|
|
201
|
+
if (this.options.l1?.enabled !== false) {
|
|
202
|
+
const l1Value = this.l1Cache.get(fullKey);
|
|
203
|
+
if (l1Value !== undefined) {
|
|
204
|
+
this.stats.hits++;
|
|
205
|
+
this.stats.l1Hits++;
|
|
206
|
+
return l1Value;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// If L2 is disabled, return null
|
|
210
|
+
if (this.options.l2?.enabled === false) {
|
|
211
|
+
this.stats.misses++;
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
// Try L2 (Redis cluster)
|
|
215
|
+
try {
|
|
216
|
+
const redis = this.getRedisClient();
|
|
217
|
+
if (redis) {
|
|
218
|
+
const l2Value = await redis.get(fullKey);
|
|
219
|
+
if (l2Value !== null) {
|
|
220
|
+
this.stats.hits++;
|
|
221
|
+
this.stats.l2Hits++;
|
|
222
|
+
// Update L1 cache
|
|
223
|
+
if (this.options.l1?.enabled !== false) {
|
|
224
|
+
this.l1Cache.set(fullKey, l2Value);
|
|
225
|
+
}
|
|
226
|
+
return l2Value;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
console.error('RedisClusterCache: Error getting from L2', error);
|
|
232
|
+
}
|
|
233
|
+
this.stats.misses++;
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Set a value in cache (both L1 and L2)
|
|
238
|
+
*/
|
|
239
|
+
async set(key, value, ttl) {
|
|
240
|
+
const fullKey = this.buildKey(key);
|
|
241
|
+
const l2Ttl = ttl ?? this.options.l2?.defaultTtl ?? 3600;
|
|
242
|
+
// Always set in L1
|
|
243
|
+
if (this.options.l1?.enabled !== false) {
|
|
244
|
+
this.l1Cache.set(fullKey, value);
|
|
245
|
+
}
|
|
246
|
+
// Set in L2 if enabled
|
|
247
|
+
if (this.options.l2?.enabled !== false) {
|
|
248
|
+
try {
|
|
249
|
+
const redis = this.getRedisClient();
|
|
250
|
+
if (redis) {
|
|
251
|
+
await redis.setex(fullKey, l2Ttl, value);
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
console.error('RedisClusterCache: Error setting in L2', error);
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Delete a key from cache (both L1 and L2)
|
|
264
|
+
*/
|
|
265
|
+
async del(key) {
|
|
266
|
+
const fullKey = this.buildKey(key);
|
|
267
|
+
// Delete from L1
|
|
268
|
+
if (this.options.l1?.enabled !== false) {
|
|
269
|
+
this.l1Cache.delete(fullKey);
|
|
270
|
+
}
|
|
271
|
+
// Delete from L2 if enabled
|
|
272
|
+
if (this.options.l2?.enabled !== false) {
|
|
273
|
+
try {
|
|
274
|
+
const redis = this.getRedisClient();
|
|
275
|
+
if (redis) {
|
|
276
|
+
await redis.del(fullKey);
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
console.error('RedisClusterCache: Error deleting from L2', error);
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Check if a key exists in cache
|
|
289
|
+
*/
|
|
290
|
+
async exists(key) {
|
|
291
|
+
const fullKey = this.buildKey(key);
|
|
292
|
+
// Check L1 first
|
|
293
|
+
if (this.options.l1?.enabled !== false) {
|
|
294
|
+
if (this.l1Cache.has(fullKey)) {
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
// Check L2 if enabled
|
|
299
|
+
if (this.options.l2?.enabled !== false) {
|
|
300
|
+
try {
|
|
301
|
+
const redis = this.getRedisClient();
|
|
302
|
+
if (redis) {
|
|
303
|
+
const result = await redis.exists(fullKey);
|
|
304
|
+
return result === 1;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
console.error('RedisClusterCache: Error checking existence in L2', error);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Invalidate all keys matching a pattern
|
|
315
|
+
*/
|
|
316
|
+
async invalidatePattern(pattern) {
|
|
317
|
+
const fullPattern = this.buildKey(pattern);
|
|
318
|
+
let invalidated = 0;
|
|
319
|
+
// Clear matching keys from L1
|
|
320
|
+
if (this.options.l1?.enabled !== false) {
|
|
321
|
+
const l1Keys = this.l1Cache.keys();
|
|
322
|
+
const regex = this.patternToRegex(fullPattern);
|
|
323
|
+
for (const key of l1Keys) {
|
|
324
|
+
if (regex.test(key)) {
|
|
325
|
+
this.l1Cache.delete(key);
|
|
326
|
+
invalidated++;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// Clear matching keys from L2 if enabled
|
|
331
|
+
if (this.options.l2?.enabled !== false) {
|
|
332
|
+
try {
|
|
333
|
+
const redis = this.getRedisClient();
|
|
334
|
+
if (redis) {
|
|
335
|
+
const keys = await redis.keys(fullPattern);
|
|
336
|
+
if (keys.length > 0) {
|
|
337
|
+
await redis.del(...keys);
|
|
338
|
+
invalidated += keys.length;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
catch (error) {
|
|
343
|
+
console.error('RedisClusterCache: Error invalidating pattern in L2', error);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return invalidated;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Clear all cache entries
|
|
350
|
+
*/
|
|
351
|
+
async clear() {
|
|
352
|
+
// Clear L1
|
|
353
|
+
if (this.options.l1?.enabled !== false) {
|
|
354
|
+
this.l1Cache.clear();
|
|
355
|
+
}
|
|
356
|
+
// Clear L2 if enabled
|
|
357
|
+
if (this.options.l2?.enabled !== false) {
|
|
358
|
+
try {
|
|
359
|
+
const redis = this.getRedisClient();
|
|
360
|
+
if (redis) {
|
|
361
|
+
const keys = await redis.keys(this.buildKey('*'));
|
|
362
|
+
if (keys.length > 0) {
|
|
363
|
+
await redis.del(...keys);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
console.error('RedisClusterCache: Error clearing L2', error);
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
this.resetStats();
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Get multiple keys at once
|
|
377
|
+
*/
|
|
378
|
+
async mget(keys) {
|
|
379
|
+
const fullKeys = keys.map(k => this.buildKey(k));
|
|
380
|
+
const results = new Array(keys.length);
|
|
381
|
+
// Separate keys into L1 and L2
|
|
382
|
+
const l1Results = new Map();
|
|
383
|
+
const l2Keys = [];
|
|
384
|
+
if (this.options.l1?.enabled !== false) {
|
|
385
|
+
for (let i = 0; i < fullKeys.length; i++) {
|
|
386
|
+
const value = this.l1Cache.get(fullKeys[i]);
|
|
387
|
+
if (value !== undefined) {
|
|
388
|
+
results[i] = value;
|
|
389
|
+
this.stats.hits++;
|
|
390
|
+
this.stats.l1Hits++;
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
l2Keys.push(fullKeys[i]);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
l2Keys.push(...fullKeys);
|
|
399
|
+
}
|
|
400
|
+
// Get remaining from L2
|
|
401
|
+
if (l2Keys.length > 0 && this.options.l2?.enabled !== false) {
|
|
402
|
+
try {
|
|
403
|
+
const redis = this.getRedisClient();
|
|
404
|
+
if (redis) {
|
|
405
|
+
const l2Results = await redis.mget(...l2Keys);
|
|
406
|
+
for (let i = 0; i < l2Keys.length; i++) {
|
|
407
|
+
const fullKey = l2Keys[i];
|
|
408
|
+
const originalIndex = fullKeys.indexOf(fullKey);
|
|
409
|
+
if (l2Results[i] !== null) {
|
|
410
|
+
results[originalIndex] = l2Results[i];
|
|
411
|
+
this.stats.hits++;
|
|
412
|
+
this.stats.l2Hits++;
|
|
413
|
+
// Update L1 cache
|
|
414
|
+
if (this.options.l1?.enabled !== false) {
|
|
415
|
+
this.l1Cache.set(fullKey, l2Results[i]);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
this.stats.misses++;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
catch (error) {
|
|
425
|
+
console.error('RedisClusterCache: Error in mget L2', error);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return results;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Set multiple keys at once
|
|
432
|
+
*/
|
|
433
|
+
async mset(entries) {
|
|
434
|
+
// Set in L1
|
|
435
|
+
if (this.options.l1?.enabled !== false) {
|
|
436
|
+
for (const entry of entries) {
|
|
437
|
+
this.l1Cache.set(this.buildKey(entry.key), entry.value);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
// Set in L2 if enabled
|
|
441
|
+
if (this.options.l2?.enabled !== false) {
|
|
442
|
+
try {
|
|
443
|
+
const redis = this.getRedisClient();
|
|
444
|
+
if (redis) {
|
|
445
|
+
const pipeline = redis.pipeline();
|
|
446
|
+
for (const entry of entries) {
|
|
447
|
+
const fullKey = this.buildKey(entry.key);
|
|
448
|
+
const l2Ttl = entry.ttl ?? this.options.l2?.defaultTtl ?? 3600;
|
|
449
|
+
pipeline.setex(fullKey, l2Ttl, entry.value);
|
|
450
|
+
}
|
|
451
|
+
await pipeline.exec();
|
|
452
|
+
return true;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
catch (error) {
|
|
456
|
+
console.error('RedisClusterCache: Error in mset L2', error);
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return true;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Get the appropriate Redis client (master or replica)
|
|
464
|
+
*/
|
|
465
|
+
getRedisClient() {
|
|
466
|
+
if (!this.redisCluster) {
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
// Use replica if enabled and available
|
|
470
|
+
if (this.options.readFromReplicas && this.redisReplicas.length > 0) {
|
|
471
|
+
const randomIndex = Math.floor(Math.random() * this.redisReplicas.length);
|
|
472
|
+
return this.redisReplicas[randomIndex];
|
|
473
|
+
}
|
|
474
|
+
return this.redisCluster;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Get cache statistics
|
|
478
|
+
*/
|
|
479
|
+
getStats() {
|
|
480
|
+
return {
|
|
481
|
+
...super.getStats(),
|
|
482
|
+
keys: this.l1Cache.size(),
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Close all Redis connections
|
|
487
|
+
*/
|
|
488
|
+
async disconnect() {
|
|
489
|
+
// Close replica connections
|
|
490
|
+
for (const replica of this.redisReplicas) {
|
|
491
|
+
try {
|
|
492
|
+
await replica.quit();
|
|
493
|
+
}
|
|
494
|
+
catch (error) {
|
|
495
|
+
console.error('RedisClusterCache: Error closing replica', error);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
this.redisReplicas = [];
|
|
499
|
+
// Close cluster connection
|
|
500
|
+
if (this.redisCluster) {
|
|
501
|
+
try {
|
|
502
|
+
await this.redisCluster.quit();
|
|
503
|
+
}
|
|
504
|
+
catch (error) {
|
|
505
|
+
console.error('RedisClusterCache: Error closing cluster', error);
|
|
506
|
+
}
|
|
507
|
+
this.redisCluster = null;
|
|
508
|
+
}
|
|
509
|
+
this.connected = false;
|
|
510
|
+
console.log('RedisClusterCache: Disconnected');
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Check if cache backend is connected
|
|
514
|
+
*/
|
|
515
|
+
isConnected() {
|
|
516
|
+
return this.connected && this.redisCluster !== null;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Get the underlying Redis cluster instance
|
|
520
|
+
*/
|
|
521
|
+
getRedisCluster() {
|
|
522
|
+
return this.redisCluster;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Get all replica connections
|
|
526
|
+
*/
|
|
527
|
+
getReplicas() {
|
|
528
|
+
return this.redisReplicas;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Manually invalidate L1 cache (useful for testing)
|
|
532
|
+
*/
|
|
533
|
+
clearL1() {
|
|
534
|
+
this.l1Cache.clear();
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Convert glob pattern to regex
|
|
538
|
+
*/
|
|
539
|
+
patternToRegex(pattern) {
|
|
540
|
+
const escaped = pattern
|
|
541
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
542
|
+
.replace(/\*/g, '.*')
|
|
543
|
+
.replace(/\?/g, '.');
|
|
544
|
+
return new RegExp(`^${escaped}$`);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
exports.RedisClusterCache = RedisClusterCache;
|
|
548
|
+
/**
|
|
549
|
+
* Create a RedisClusterCache instance with simplified options
|
|
550
|
+
*/
|
|
551
|
+
function createRedisClusterCache(nodes, options = {}) {
|
|
552
|
+
return new RedisClusterCache({
|
|
553
|
+
...options,
|
|
554
|
+
clusterNodes: nodes,
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
exports.default = RedisClusterCache;
|