@dexto/storage 1.6.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/LICENSE +44 -0
- package/README.md +80 -0
- package/dist/blob/factories/index.cjs +31 -0
- package/dist/blob/factories/index.d.cts +6 -0
- package/dist/blob/factories/index.d.ts +6 -0
- package/dist/blob/factories/index.d.ts.map +1 -0
- package/dist/blob/factories/index.js +6 -0
- package/dist/blob/factories/local.cjs +38 -0
- package/dist/blob/factories/local.d.cts +21 -0
- package/dist/blob/factories/local.d.ts +17 -0
- package/dist/blob/factories/local.d.ts.map +1 -0
- package/dist/blob/factories/local.js +14 -0
- package/dist/blob/factories/memory.cjs +44 -0
- package/dist/blob/factories/memory.d.cts +21 -0
- package/dist/blob/factories/memory.d.ts +17 -0
- package/dist/blob/factories/memory.d.ts.map +1 -0
- package/dist/blob/factories/memory.js +20 -0
- package/dist/blob/factory.cjs +16 -0
- package/dist/blob/factory.d.cts +36 -0
- package/dist/blob/factory.d.ts +35 -0
- package/dist/blob/factory.d.ts.map +1 -0
- package/dist/blob/factory.js +0 -0
- package/dist/blob/index.cjs +45 -0
- package/dist/blob/index.d.cts +8 -0
- package/dist/blob/index.d.ts +26 -0
- package/dist/blob/index.d.ts.map +1 -0
- package/dist/blob/index.js +19 -0
- package/dist/blob/local-blob-store.cjs +532 -0
- package/dist/blob/local-blob-store.d.cts +56 -0
- package/dist/blob/local-blob-store.d.ts +54 -0
- package/dist/blob/local-blob-store.d.ts.map +1 -0
- package/dist/blob/local-blob-store.js +498 -0
- package/dist/blob/memory-blob-store.cjs +327 -0
- package/dist/blob/memory-blob-store.d.cts +69 -0
- package/dist/blob/memory-blob-store.d.ts +67 -0
- package/dist/blob/memory-blob-store.d.ts.map +1 -0
- package/dist/blob/memory-blob-store.js +303 -0
- package/dist/blob/schemas.cjs +52 -0
- package/dist/blob/schemas.d.cts +87 -0
- package/dist/blob/schemas.d.ts +86 -0
- package/dist/blob/schemas.d.ts.map +1 -0
- package/dist/blob/schemas.js +25 -0
- package/dist/blob/types.cjs +16 -0
- package/dist/blob/types.d.cts +1 -0
- package/dist/blob/types.d.ts +2 -0
- package/dist/blob/types.d.ts.map +1 -0
- package/dist/blob/types.js +0 -0
- package/dist/cache/factories/index.cjs +31 -0
- package/dist/cache/factories/index.d.cts +6 -0
- package/dist/cache/factories/index.d.ts +6 -0
- package/dist/cache/factories/index.d.ts.map +1 -0
- package/dist/cache/factories/index.js +6 -0
- package/dist/cache/factories/memory.cjs +39 -0
- package/dist/cache/factories/memory.d.cts +21 -0
- package/dist/cache/factories/memory.d.ts +17 -0
- package/dist/cache/factories/memory.d.ts.map +1 -0
- package/dist/cache/factories/memory.js +15 -0
- package/dist/cache/factories/redis.cjs +65 -0
- package/dist/cache/factories/redis.d.cts +24 -0
- package/dist/cache/factories/redis.d.ts +20 -0
- package/dist/cache/factories/redis.d.ts.map +1 -0
- package/dist/cache/factories/redis.js +31 -0
- package/dist/cache/factory.cjs +16 -0
- package/dist/cache/factory.d.cts +42 -0
- package/dist/cache/factory.d.ts +41 -0
- package/dist/cache/factory.d.ts.map +1 -0
- package/dist/cache/factory.js +0 -0
- package/dist/cache/index.cjs +42 -0
- package/dist/cache/index.d.cts +7 -0
- package/dist/cache/index.d.ts +25 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +17 -0
- package/dist/cache/memory-cache-store.cjs +106 -0
- package/dist/cache/memory-cache-store.d.cts +27 -0
- package/dist/cache/memory-cache-store.d.ts +25 -0
- package/dist/cache/memory-cache-store.d.ts.map +1 -0
- package/dist/cache/memory-cache-store.js +82 -0
- package/dist/cache/redis-store.cjs +176 -0
- package/dist/cache/redis-store.d.cts +34 -0
- package/dist/cache/redis-store.d.ts +32 -0
- package/dist/cache/redis-store.d.ts.map +1 -0
- package/dist/cache/redis-store.js +152 -0
- package/dist/cache/schemas.cjs +70 -0
- package/dist/cache/schemas.d.cts +93 -0
- package/dist/cache/schemas.d.ts +91 -0
- package/dist/cache/schemas.d.ts.map +1 -0
- package/dist/cache/schemas.js +43 -0
- package/dist/cache/types.cjs +16 -0
- package/dist/cache/types.d.cts +1 -0
- package/dist/cache/types.d.ts +2 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache/types.js +0 -0
- package/dist/database/factories/index.cjs +34 -0
- package/dist/database/factories/index.d.cts +7 -0
- package/dist/database/factories/index.d.ts +7 -0
- package/dist/database/factories/index.d.ts.map +1 -0
- package/dist/database/factories/index.js +8 -0
- package/dist/database/factories/memory.cjs +39 -0
- package/dist/database/factories/memory.d.cts +20 -0
- package/dist/database/factories/memory.d.ts +16 -0
- package/dist/database/factories/memory.d.ts.map +1 -0
- package/dist/database/factories/memory.js +15 -0
- package/dist/database/factories/postgres.cjs +61 -0
- package/dist/database/factories/postgres.d.cts +23 -0
- package/dist/database/factories/postgres.d.ts +19 -0
- package/dist/database/factories/postgres.d.ts.map +1 -0
- package/dist/database/factories/postgres.js +27 -0
- package/dist/database/factories/sqlite.cjs +65 -0
- package/dist/database/factories/sqlite.d.cts +24 -0
- package/dist/database/factories/sqlite.d.ts +20 -0
- package/dist/database/factories/sqlite.d.ts.map +1 -0
- package/dist/database/factories/sqlite.js +31 -0
- package/dist/database/factory.cjs +16 -0
- package/dist/database/factory.d.cts +42 -0
- package/dist/database/factory.d.ts +41 -0
- package/dist/database/factory.d.ts.map +1 -0
- package/dist/database/factory.js +0 -0
- package/dist/database/index.cjs +46 -0
- package/dist/database/index.d.cts +8 -0
- package/dist/database/index.d.ts +26 -0
- package/dist/database/index.d.ts.map +1 -0
- package/dist/database/index.js +24 -0
- package/dist/database/memory-database-store.cjs +121 -0
- package/dist/database/memory-database-store.d.cts +30 -0
- package/dist/database/memory-database-store.d.ts +28 -0
- package/dist/database/memory-database-store.d.ts.map +1 -0
- package/dist/database/memory-database-store.js +97 -0
- package/dist/database/postgres-store.cjs +342 -0
- package/dist/database/postgres-store.d.cts +57 -0
- package/dist/database/postgres-store.d.ts +55 -0
- package/dist/database/postgres-store.d.ts.map +1 -0
- package/dist/database/postgres-store.js +318 -0
- package/dist/database/schemas.cjs +82 -0
- package/dist/database/schemas.d.cts +127 -0
- package/dist/database/schemas.d.ts +125 -0
- package/dist/database/schemas.d.ts.map +1 -0
- package/dist/database/schemas.js +54 -0
- package/dist/database/sqlite-store.cjs +270 -0
- package/dist/database/sqlite-store.d.cts +35 -0
- package/dist/database/sqlite-store.d.ts +33 -0
- package/dist/database/sqlite-store.d.ts.map +1 -0
- package/dist/database/sqlite-store.js +236 -0
- package/dist/database/types.cjs +16 -0
- package/dist/database/types.d.cts +1 -0
- package/dist/database/types.d.ts +2 -0
- package/dist/database/types.d.ts.map +1 -0
- package/dist/database/types.js +0 -0
- package/dist/index.cjs +82 -0
- package/dist/index.d.cts +24 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +50 -0
- package/dist/schemas.cjs +67 -0
- package/dist/schemas.d.cts +72 -0
- package/dist/schemas.d.ts +70 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +46 -0
- package/package.json +55 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { StorageError } from "@dexto/core";
|
|
2
|
+
class MemoryCacheStore {
|
|
3
|
+
data = /* @__PURE__ */ new Map();
|
|
4
|
+
ttls = /* @__PURE__ */ new Map();
|
|
5
|
+
connected = false;
|
|
6
|
+
async connect() {
|
|
7
|
+
if (this.connected) return;
|
|
8
|
+
this.connected = true;
|
|
9
|
+
}
|
|
10
|
+
async disconnect() {
|
|
11
|
+
this.connected = false;
|
|
12
|
+
this.data.clear();
|
|
13
|
+
this.ttls.clear();
|
|
14
|
+
}
|
|
15
|
+
isConnected() {
|
|
16
|
+
return this.connected;
|
|
17
|
+
}
|
|
18
|
+
getStoreType() {
|
|
19
|
+
return "in-memory";
|
|
20
|
+
}
|
|
21
|
+
async get(key) {
|
|
22
|
+
this.checkConnection();
|
|
23
|
+
try {
|
|
24
|
+
this.checkTTL(key);
|
|
25
|
+
return this.data.get(key);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
throw StorageError.readFailed(
|
|
28
|
+
"get",
|
|
29
|
+
error instanceof Error ? error.message : String(error),
|
|
30
|
+
{ key }
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async set(key, value, ttlSeconds) {
|
|
35
|
+
this.checkConnection();
|
|
36
|
+
try {
|
|
37
|
+
this.data.set(key, value);
|
|
38
|
+
if (ttlSeconds) {
|
|
39
|
+
this.ttls.set(key, Date.now() + ttlSeconds * 1e3);
|
|
40
|
+
} else {
|
|
41
|
+
this.ttls.delete(key);
|
|
42
|
+
}
|
|
43
|
+
} catch (error) {
|
|
44
|
+
throw StorageError.writeFailed(
|
|
45
|
+
"set",
|
|
46
|
+
error instanceof Error ? error.message : String(error),
|
|
47
|
+
{ key }
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async delete(key) {
|
|
52
|
+
this.checkConnection();
|
|
53
|
+
this.data.delete(key);
|
|
54
|
+
this.ttls.delete(key);
|
|
55
|
+
}
|
|
56
|
+
// Helper methods
|
|
57
|
+
checkConnection() {
|
|
58
|
+
if (!this.connected) {
|
|
59
|
+
throw StorageError.notConnected("MemoryCacheStore");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
checkTTL(key) {
|
|
63
|
+
const expiry = this.ttls.get(key);
|
|
64
|
+
if (expiry && Date.now() > expiry) {
|
|
65
|
+
this.data.delete(key);
|
|
66
|
+
this.ttls.delete(key);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Development helpers
|
|
70
|
+
async clear() {
|
|
71
|
+
this.data.clear();
|
|
72
|
+
this.ttls.clear();
|
|
73
|
+
}
|
|
74
|
+
async dump() {
|
|
75
|
+
return {
|
|
76
|
+
data: Object.fromEntries(this.data.entries())
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export {
|
|
81
|
+
MemoryCacheStore
|
|
82
|
+
};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var redis_store_exports = {};
|
|
20
|
+
__export(redis_store_exports, {
|
|
21
|
+
RedisStore: () => RedisStore
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(redis_store_exports);
|
|
24
|
+
var import_ioredis = require("ioredis");
|
|
25
|
+
var import_core = require("@dexto/core");
|
|
26
|
+
class RedisStore {
|
|
27
|
+
constructor(config, logger) {
|
|
28
|
+
this.config = config;
|
|
29
|
+
this.logger = logger.createChild(import_core.DextoLogComponent.STORAGE);
|
|
30
|
+
}
|
|
31
|
+
redis = null;
|
|
32
|
+
connected = false;
|
|
33
|
+
logger;
|
|
34
|
+
async connect() {
|
|
35
|
+
if (this.connected) return;
|
|
36
|
+
this.redis = new import_ioredis.Redis({
|
|
37
|
+
...this.config.host && { host: this.config.host },
|
|
38
|
+
...this.config.port && { port: this.config.port },
|
|
39
|
+
...this.config.password && { password: this.config.password },
|
|
40
|
+
db: this.config.database || 0,
|
|
41
|
+
family: 4,
|
|
42
|
+
// IPv4 by default
|
|
43
|
+
...this.config.connectionTimeoutMillis && {
|
|
44
|
+
connectTimeout: this.config.connectionTimeoutMillis
|
|
45
|
+
},
|
|
46
|
+
...this.config.connectionTimeoutMillis && {
|
|
47
|
+
commandTimeout: this.config.connectionTimeoutMillis
|
|
48
|
+
},
|
|
49
|
+
maxRetriesPerRequest: 3,
|
|
50
|
+
lazyConnect: true,
|
|
51
|
+
...this.config.options
|
|
52
|
+
});
|
|
53
|
+
this.redis.on("error", (error) => {
|
|
54
|
+
if (error instanceof Error) {
|
|
55
|
+
this.logger.trackException(error, { operation: "redis.connection_error" });
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.logger.error("Redis connection error", { error: String(error) });
|
|
59
|
+
});
|
|
60
|
+
this.redis.on("connect", () => {
|
|
61
|
+
this.connected = true;
|
|
62
|
+
});
|
|
63
|
+
this.redis.on("close", () => {
|
|
64
|
+
this.connected = false;
|
|
65
|
+
});
|
|
66
|
+
await this.redis.connect();
|
|
67
|
+
}
|
|
68
|
+
async disconnect() {
|
|
69
|
+
if (this.redis) {
|
|
70
|
+
await this.redis.quit();
|
|
71
|
+
this.redis = null;
|
|
72
|
+
}
|
|
73
|
+
this.connected = false;
|
|
74
|
+
}
|
|
75
|
+
isConnected() {
|
|
76
|
+
return this.connected && this.redis?.status === "ready";
|
|
77
|
+
}
|
|
78
|
+
getStoreType() {
|
|
79
|
+
return "redis";
|
|
80
|
+
}
|
|
81
|
+
// Core operations
|
|
82
|
+
async get(key) {
|
|
83
|
+
this.checkConnection();
|
|
84
|
+
try {
|
|
85
|
+
const value = await this.redis.get(key);
|
|
86
|
+
return value ? JSON.parse(value) : void 0;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
throw import_core.StorageError.readFailed(
|
|
89
|
+
"get",
|
|
90
|
+
error instanceof Error ? error.message : String(error),
|
|
91
|
+
{ key }
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async set(key, value, ttlSeconds) {
|
|
96
|
+
this.checkConnection();
|
|
97
|
+
try {
|
|
98
|
+
const serialized = JSON.stringify(value);
|
|
99
|
+
if (ttlSeconds) {
|
|
100
|
+
await this.redis.setex(key, ttlSeconds, serialized);
|
|
101
|
+
} else {
|
|
102
|
+
await this.redis.set(key, serialized);
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
throw import_core.StorageError.writeFailed(
|
|
106
|
+
"set",
|
|
107
|
+
error instanceof Error ? error.message : String(error),
|
|
108
|
+
{ key }
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async delete(key) {
|
|
113
|
+
this.checkConnection();
|
|
114
|
+
try {
|
|
115
|
+
await this.redis.del(key);
|
|
116
|
+
} catch (error) {
|
|
117
|
+
throw import_core.StorageError.deleteFailed(
|
|
118
|
+
"delete",
|
|
119
|
+
error instanceof Error ? error.message : String(error),
|
|
120
|
+
{ key }
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Redis-specific optimizations
|
|
125
|
+
async mget(keys) {
|
|
126
|
+
this.checkConnection();
|
|
127
|
+
if (keys.length === 0) return [];
|
|
128
|
+
const values = await this.redis.mget(...keys);
|
|
129
|
+
return values.map((value) => value ? JSON.parse(value) : void 0);
|
|
130
|
+
}
|
|
131
|
+
async mset(entries) {
|
|
132
|
+
this.checkConnection();
|
|
133
|
+
if (entries.length === 0) return;
|
|
134
|
+
const pipeline = this.redis.pipeline();
|
|
135
|
+
for (const [key, value] of entries) {
|
|
136
|
+
pipeline.set(key, JSON.stringify(value));
|
|
137
|
+
}
|
|
138
|
+
await pipeline.exec();
|
|
139
|
+
}
|
|
140
|
+
async exists(key) {
|
|
141
|
+
this.checkConnection();
|
|
142
|
+
const result = await this.redis.exists(key);
|
|
143
|
+
return result === 1;
|
|
144
|
+
}
|
|
145
|
+
async expire(key, ttlSeconds) {
|
|
146
|
+
this.checkConnection();
|
|
147
|
+
await this.redis.expire(key, ttlSeconds);
|
|
148
|
+
}
|
|
149
|
+
// Cache-specific operations
|
|
150
|
+
async increment(key, by = 1) {
|
|
151
|
+
this.checkConnection();
|
|
152
|
+
return await this.redis.incrby(key, by);
|
|
153
|
+
}
|
|
154
|
+
async decrement(key, by = 1) {
|
|
155
|
+
this.checkConnection();
|
|
156
|
+
return await this.redis.decrby(key, by);
|
|
157
|
+
}
|
|
158
|
+
checkConnection() {
|
|
159
|
+
if (!this.connected || !this.redis || this.redis.status !== "ready") {
|
|
160
|
+
throw import_core.StorageError.notConnected("RedisStore");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Maintenance operations
|
|
164
|
+
async flushdb() {
|
|
165
|
+
this.checkConnection();
|
|
166
|
+
await this.redis.flushdb();
|
|
167
|
+
}
|
|
168
|
+
async info() {
|
|
169
|
+
this.checkConnection();
|
|
170
|
+
return await this.redis.info();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
174
|
+
0 && (module.exports = {
|
|
175
|
+
RedisStore
|
|
176
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Cache, Logger } from '@dexto/core';
|
|
2
|
+
import { RedisCacheConfig } from './schemas.cjs';
|
|
3
|
+
import 'zod';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Redis cache store for production cache operations.
|
|
7
|
+
* Implements the Cache interface with connection pooling and optimizations.
|
|
8
|
+
* EXPERIMENTAL - NOT FULLY TESTED YET
|
|
9
|
+
*/
|
|
10
|
+
declare class RedisStore implements Cache {
|
|
11
|
+
private config;
|
|
12
|
+
private redis;
|
|
13
|
+
private connected;
|
|
14
|
+
private logger;
|
|
15
|
+
constructor(config: RedisCacheConfig, logger: Logger);
|
|
16
|
+
connect(): Promise<void>;
|
|
17
|
+
disconnect(): Promise<void>;
|
|
18
|
+
isConnected(): boolean;
|
|
19
|
+
getStoreType(): string;
|
|
20
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
21
|
+
set<T>(key: string, value: T, ttlSeconds?: number): Promise<void>;
|
|
22
|
+
delete(key: string): Promise<void>;
|
|
23
|
+
mget<T>(keys: string[]): Promise<(T | undefined)[]>;
|
|
24
|
+
mset<T>(entries: [string, T][]): Promise<void>;
|
|
25
|
+
exists(key: string): Promise<boolean>;
|
|
26
|
+
expire(key: string, ttlSeconds: number): Promise<void>;
|
|
27
|
+
increment(key: string, by?: number): Promise<number>;
|
|
28
|
+
decrement(key: string, by?: number): Promise<number>;
|
|
29
|
+
private checkConnection;
|
|
30
|
+
flushdb(): Promise<void>;
|
|
31
|
+
info(): Promise<string>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { RedisStore };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Cache } from './types.js';
|
|
2
|
+
import type { RedisCacheConfig } from './schemas.js';
|
|
3
|
+
import type { Logger } from '@dexto/core';
|
|
4
|
+
/**
|
|
5
|
+
* Redis cache store for production cache operations.
|
|
6
|
+
* Implements the Cache interface with connection pooling and optimizations.
|
|
7
|
+
* EXPERIMENTAL - NOT FULLY TESTED YET
|
|
8
|
+
*/
|
|
9
|
+
export declare class RedisStore implements Cache {
|
|
10
|
+
private config;
|
|
11
|
+
private redis;
|
|
12
|
+
private connected;
|
|
13
|
+
private logger;
|
|
14
|
+
constructor(config: RedisCacheConfig, logger: Logger);
|
|
15
|
+
connect(): Promise<void>;
|
|
16
|
+
disconnect(): Promise<void>;
|
|
17
|
+
isConnected(): boolean;
|
|
18
|
+
getStoreType(): string;
|
|
19
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
20
|
+
set<T>(key: string, value: T, ttlSeconds?: number): Promise<void>;
|
|
21
|
+
delete(key: string): Promise<void>;
|
|
22
|
+
mget<T>(keys: string[]): Promise<(T | undefined)[]>;
|
|
23
|
+
mset<T>(entries: [string, T][]): Promise<void>;
|
|
24
|
+
exists(key: string): Promise<boolean>;
|
|
25
|
+
expire(key: string, ttlSeconds: number): Promise<void>;
|
|
26
|
+
increment(key: string, by?: number): Promise<number>;
|
|
27
|
+
decrement(key: string, by?: number): Promise<number>;
|
|
28
|
+
private checkConnection;
|
|
29
|
+
flushdb(): Promise<void>;
|
|
30
|
+
info(): Promise<string>;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=redis-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-store.d.ts","sourceRoot":"","sources":["../../src/cache/redis-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C;;;;GAIG;AACH,qBAAa,UAAW,YAAW,KAAK;IAMhC,OAAO,CAAC,MAAM;IALlB,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAS;gBAGX,MAAM,EAAE,gBAAgB,EAChC,MAAM,EAAE,MAAM;IAKZ,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAwCxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAQjC,WAAW,IAAI,OAAO;IAItB,YAAY,IAAI,MAAM;IAKhB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAc3C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBjE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAclC,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,EAAE,CAAC;IAQnD,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAW9C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAMrC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtD,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,GAAE,MAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IAKvD,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,GAAE,MAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IAK7D,OAAO,CAAC,eAAe;IAOjB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAKxB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAIhC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { Redis } from "ioredis";
|
|
2
|
+
import { DextoLogComponent, StorageError } from "@dexto/core";
|
|
3
|
+
class RedisStore {
|
|
4
|
+
constructor(config, logger) {
|
|
5
|
+
this.config = config;
|
|
6
|
+
this.logger = logger.createChild(DextoLogComponent.STORAGE);
|
|
7
|
+
}
|
|
8
|
+
redis = null;
|
|
9
|
+
connected = false;
|
|
10
|
+
logger;
|
|
11
|
+
async connect() {
|
|
12
|
+
if (this.connected) return;
|
|
13
|
+
this.redis = new Redis({
|
|
14
|
+
...this.config.host && { host: this.config.host },
|
|
15
|
+
...this.config.port && { port: this.config.port },
|
|
16
|
+
...this.config.password && { password: this.config.password },
|
|
17
|
+
db: this.config.database || 0,
|
|
18
|
+
family: 4,
|
|
19
|
+
// IPv4 by default
|
|
20
|
+
...this.config.connectionTimeoutMillis && {
|
|
21
|
+
connectTimeout: this.config.connectionTimeoutMillis
|
|
22
|
+
},
|
|
23
|
+
...this.config.connectionTimeoutMillis && {
|
|
24
|
+
commandTimeout: this.config.connectionTimeoutMillis
|
|
25
|
+
},
|
|
26
|
+
maxRetriesPerRequest: 3,
|
|
27
|
+
lazyConnect: true,
|
|
28
|
+
...this.config.options
|
|
29
|
+
});
|
|
30
|
+
this.redis.on("error", (error) => {
|
|
31
|
+
if (error instanceof Error) {
|
|
32
|
+
this.logger.trackException(error, { operation: "redis.connection_error" });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
this.logger.error("Redis connection error", { error: String(error) });
|
|
36
|
+
});
|
|
37
|
+
this.redis.on("connect", () => {
|
|
38
|
+
this.connected = true;
|
|
39
|
+
});
|
|
40
|
+
this.redis.on("close", () => {
|
|
41
|
+
this.connected = false;
|
|
42
|
+
});
|
|
43
|
+
await this.redis.connect();
|
|
44
|
+
}
|
|
45
|
+
async disconnect() {
|
|
46
|
+
if (this.redis) {
|
|
47
|
+
await this.redis.quit();
|
|
48
|
+
this.redis = null;
|
|
49
|
+
}
|
|
50
|
+
this.connected = false;
|
|
51
|
+
}
|
|
52
|
+
isConnected() {
|
|
53
|
+
return this.connected && this.redis?.status === "ready";
|
|
54
|
+
}
|
|
55
|
+
getStoreType() {
|
|
56
|
+
return "redis";
|
|
57
|
+
}
|
|
58
|
+
// Core operations
|
|
59
|
+
async get(key) {
|
|
60
|
+
this.checkConnection();
|
|
61
|
+
try {
|
|
62
|
+
const value = await this.redis.get(key);
|
|
63
|
+
return value ? JSON.parse(value) : void 0;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
throw StorageError.readFailed(
|
|
66
|
+
"get",
|
|
67
|
+
error instanceof Error ? error.message : String(error),
|
|
68
|
+
{ key }
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async set(key, value, ttlSeconds) {
|
|
73
|
+
this.checkConnection();
|
|
74
|
+
try {
|
|
75
|
+
const serialized = JSON.stringify(value);
|
|
76
|
+
if (ttlSeconds) {
|
|
77
|
+
await this.redis.setex(key, ttlSeconds, serialized);
|
|
78
|
+
} else {
|
|
79
|
+
await this.redis.set(key, serialized);
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
throw StorageError.writeFailed(
|
|
83
|
+
"set",
|
|
84
|
+
error instanceof Error ? error.message : String(error),
|
|
85
|
+
{ key }
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async delete(key) {
|
|
90
|
+
this.checkConnection();
|
|
91
|
+
try {
|
|
92
|
+
await this.redis.del(key);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
throw StorageError.deleteFailed(
|
|
95
|
+
"delete",
|
|
96
|
+
error instanceof Error ? error.message : String(error),
|
|
97
|
+
{ key }
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Redis-specific optimizations
|
|
102
|
+
async mget(keys) {
|
|
103
|
+
this.checkConnection();
|
|
104
|
+
if (keys.length === 0) return [];
|
|
105
|
+
const values = await this.redis.mget(...keys);
|
|
106
|
+
return values.map((value) => value ? JSON.parse(value) : void 0);
|
|
107
|
+
}
|
|
108
|
+
async mset(entries) {
|
|
109
|
+
this.checkConnection();
|
|
110
|
+
if (entries.length === 0) return;
|
|
111
|
+
const pipeline = this.redis.pipeline();
|
|
112
|
+
for (const [key, value] of entries) {
|
|
113
|
+
pipeline.set(key, JSON.stringify(value));
|
|
114
|
+
}
|
|
115
|
+
await pipeline.exec();
|
|
116
|
+
}
|
|
117
|
+
async exists(key) {
|
|
118
|
+
this.checkConnection();
|
|
119
|
+
const result = await this.redis.exists(key);
|
|
120
|
+
return result === 1;
|
|
121
|
+
}
|
|
122
|
+
async expire(key, ttlSeconds) {
|
|
123
|
+
this.checkConnection();
|
|
124
|
+
await this.redis.expire(key, ttlSeconds);
|
|
125
|
+
}
|
|
126
|
+
// Cache-specific operations
|
|
127
|
+
async increment(key, by = 1) {
|
|
128
|
+
this.checkConnection();
|
|
129
|
+
return await this.redis.incrby(key, by);
|
|
130
|
+
}
|
|
131
|
+
async decrement(key, by = 1) {
|
|
132
|
+
this.checkConnection();
|
|
133
|
+
return await this.redis.decrby(key, by);
|
|
134
|
+
}
|
|
135
|
+
checkConnection() {
|
|
136
|
+
if (!this.connected || !this.redis || this.redis.status !== "ready") {
|
|
137
|
+
throw StorageError.notConnected("RedisStore");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Maintenance operations
|
|
141
|
+
async flushdb() {
|
|
142
|
+
this.checkConnection();
|
|
143
|
+
await this.redis.flushdb();
|
|
144
|
+
}
|
|
145
|
+
async info() {
|
|
146
|
+
this.checkConnection();
|
|
147
|
+
return await this.redis.info();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
export {
|
|
151
|
+
RedisStore
|
|
152
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var schemas_exports = {};
|
|
20
|
+
__export(schemas_exports, {
|
|
21
|
+
CACHE_TYPES: () => CACHE_TYPES,
|
|
22
|
+
CacheConfigSchema: () => CacheConfigSchema,
|
|
23
|
+
InMemoryCacheSchema: () => InMemoryCacheSchema,
|
|
24
|
+
RedisCacheSchema: () => RedisCacheSchema
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(schemas_exports);
|
|
27
|
+
var import_zod = require("zod");
|
|
28
|
+
var import_core = require("@dexto/core");
|
|
29
|
+
const CACHE_TYPES = ["in-memory", "redis"];
|
|
30
|
+
const BaseCacheSchema = import_zod.z.object({
|
|
31
|
+
maxConnections: import_zod.z.number().int().positive().optional().describe("Maximum connections"),
|
|
32
|
+
idleTimeoutMillis: import_zod.z.number().int().positive().optional().describe("Idle timeout in milliseconds"),
|
|
33
|
+
connectionTimeoutMillis: import_zod.z.number().int().positive().optional().describe("Connection timeout in milliseconds"),
|
|
34
|
+
options: import_zod.z.record(import_zod.z.unknown()).optional().describe("Backend-specific options")
|
|
35
|
+
});
|
|
36
|
+
const InMemoryCacheSchema = BaseCacheSchema.extend({
|
|
37
|
+
type: import_zod.z.literal("in-memory")
|
|
38
|
+
// In-memory cache doesn't need connection options, but inherits pool options for consistency
|
|
39
|
+
}).strict();
|
|
40
|
+
const RedisCacheSchema = BaseCacheSchema.extend({
|
|
41
|
+
type: import_zod.z.literal("redis"),
|
|
42
|
+
url: (0, import_core.EnvExpandedString)().optional().describe("Redis connection URL (redis://...)"),
|
|
43
|
+
host: import_zod.z.string().optional().describe("Redis host"),
|
|
44
|
+
port: import_zod.z.number().int().positive().optional().describe("Redis port"),
|
|
45
|
+
password: import_zod.z.string().optional().describe("Redis password"),
|
|
46
|
+
database: import_zod.z.number().int().nonnegative().optional().describe("Redis database number")
|
|
47
|
+
}).strict().superRefine((data, ctx) => {
|
|
48
|
+
if (!data.url && !data.host) {
|
|
49
|
+
ctx.addIssue({
|
|
50
|
+
code: import_zod.z.ZodIssueCode.custom,
|
|
51
|
+
message: "Redis cache requires either 'url' or 'host' to be specified",
|
|
52
|
+
path: ["url"],
|
|
53
|
+
params: {
|
|
54
|
+
code: import_core.StorageErrorCode.CONNECTION_CONFIG_MISSING,
|
|
55
|
+
scope: import_core.ErrorScope.STORAGE,
|
|
56
|
+
type: import_core.ErrorType.USER
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
const CacheConfigSchema = import_zod.z.object({
|
|
62
|
+
type: import_zod.z.string().describe("Cache backend type identifier")
|
|
63
|
+
}).passthrough().describe("Cache configuration (validated by image factory)");
|
|
64
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
65
|
+
0 && (module.exports = {
|
|
66
|
+
CACHE_TYPES,
|
|
67
|
+
CacheConfigSchema,
|
|
68
|
+
InMemoryCacheSchema,
|
|
69
|
+
RedisCacheSchema
|
|
70
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
declare const CACHE_TYPES: readonly ["in-memory", "redis"];
|
|
4
|
+
type CacheType = (typeof CACHE_TYPES)[number];
|
|
5
|
+
declare const InMemoryCacheSchema: z.ZodObject<{
|
|
6
|
+
maxConnections: z.ZodOptional<z.ZodNumber>;
|
|
7
|
+
idleTimeoutMillis: z.ZodOptional<z.ZodNumber>;
|
|
8
|
+
connectionTimeoutMillis: z.ZodOptional<z.ZodNumber>;
|
|
9
|
+
options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
10
|
+
} & {
|
|
11
|
+
type: z.ZodLiteral<"in-memory">;
|
|
12
|
+
}, "strict", z.ZodTypeAny, {
|
|
13
|
+
type: "in-memory";
|
|
14
|
+
maxConnections?: number | undefined;
|
|
15
|
+
idleTimeoutMillis?: number | undefined;
|
|
16
|
+
connectionTimeoutMillis?: number | undefined;
|
|
17
|
+
options?: Record<string, unknown> | undefined;
|
|
18
|
+
}, {
|
|
19
|
+
type: "in-memory";
|
|
20
|
+
maxConnections?: number | undefined;
|
|
21
|
+
idleTimeoutMillis?: number | undefined;
|
|
22
|
+
connectionTimeoutMillis?: number | undefined;
|
|
23
|
+
options?: Record<string, unknown> | undefined;
|
|
24
|
+
}>;
|
|
25
|
+
type InMemoryCacheConfig = z.output<typeof InMemoryCacheSchema>;
|
|
26
|
+
declare const RedisCacheSchema: z.ZodEffects<z.ZodObject<{
|
|
27
|
+
maxConnections: z.ZodOptional<z.ZodNumber>;
|
|
28
|
+
idleTimeoutMillis: z.ZodOptional<z.ZodNumber>;
|
|
29
|
+
connectionTimeoutMillis: z.ZodOptional<z.ZodNumber>;
|
|
30
|
+
options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
31
|
+
} & {
|
|
32
|
+
type: z.ZodLiteral<"redis">;
|
|
33
|
+
url: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
34
|
+
host: z.ZodOptional<z.ZodString>;
|
|
35
|
+
port: z.ZodOptional<z.ZodNumber>;
|
|
36
|
+
password: z.ZodOptional<z.ZodString>;
|
|
37
|
+
database: z.ZodOptional<z.ZodNumber>;
|
|
38
|
+
}, "strict", z.ZodTypeAny, {
|
|
39
|
+
type: "redis";
|
|
40
|
+
maxConnections?: number | undefined;
|
|
41
|
+
idleTimeoutMillis?: number | undefined;
|
|
42
|
+
connectionTimeoutMillis?: number | undefined;
|
|
43
|
+
options?: Record<string, unknown> | undefined;
|
|
44
|
+
url?: string | undefined;
|
|
45
|
+
host?: string | undefined;
|
|
46
|
+
port?: number | undefined;
|
|
47
|
+
password?: string | undefined;
|
|
48
|
+
database?: number | undefined;
|
|
49
|
+
}, {
|
|
50
|
+
type: "redis";
|
|
51
|
+
maxConnections?: number | undefined;
|
|
52
|
+
idleTimeoutMillis?: number | undefined;
|
|
53
|
+
connectionTimeoutMillis?: number | undefined;
|
|
54
|
+
options?: Record<string, unknown> | undefined;
|
|
55
|
+
url?: string | undefined;
|
|
56
|
+
host?: string | undefined;
|
|
57
|
+
port?: number | undefined;
|
|
58
|
+
password?: string | undefined;
|
|
59
|
+
database?: number | undefined;
|
|
60
|
+
}>, {
|
|
61
|
+
type: "redis";
|
|
62
|
+
maxConnections?: number | undefined;
|
|
63
|
+
idleTimeoutMillis?: number | undefined;
|
|
64
|
+
connectionTimeoutMillis?: number | undefined;
|
|
65
|
+
options?: Record<string, unknown> | undefined;
|
|
66
|
+
url?: string | undefined;
|
|
67
|
+
host?: string | undefined;
|
|
68
|
+
port?: number | undefined;
|
|
69
|
+
password?: string | undefined;
|
|
70
|
+
database?: number | undefined;
|
|
71
|
+
}, {
|
|
72
|
+
type: "redis";
|
|
73
|
+
maxConnections?: number | undefined;
|
|
74
|
+
idleTimeoutMillis?: number | undefined;
|
|
75
|
+
connectionTimeoutMillis?: number | undefined;
|
|
76
|
+
options?: Record<string, unknown> | undefined;
|
|
77
|
+
url?: string | undefined;
|
|
78
|
+
host?: string | undefined;
|
|
79
|
+
port?: number | undefined;
|
|
80
|
+
password?: string | undefined;
|
|
81
|
+
database?: number | undefined;
|
|
82
|
+
}>;
|
|
83
|
+
type RedisCacheConfig = z.output<typeof RedisCacheSchema>;
|
|
84
|
+
declare const CacheConfigSchema: z.ZodObject<{
|
|
85
|
+
type: z.ZodString;
|
|
86
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
87
|
+
type: z.ZodString;
|
|
88
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
89
|
+
type: z.ZodString;
|
|
90
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
91
|
+
type CacheConfig = z.output<typeof CacheConfigSchema>;
|
|
92
|
+
|
|
93
|
+
export { CACHE_TYPES, type CacheConfig, CacheConfigSchema, type CacheType, type InMemoryCacheConfig, InMemoryCacheSchema, type RedisCacheConfig, RedisCacheSchema };
|