@mhbdev/redis 0.2.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 +9 -0
- package/LICENSE +21 -0
- package/README.md +173 -0
- package/dist/bullmq/adapter.d.ts +49 -0
- package/dist/bullmq/adapter.d.ts.map +1 -0
- package/dist/bullmq/adapter.js +56 -0
- package/dist/bullmq/adapter.js.map +1 -0
- package/dist/bullmq/index.d.ts +3 -0
- package/dist/bullmq/index.d.ts.map +1 -0
- package/dist/bullmq/index.js +2 -0
- package/dist/bullmq/index.js.map +1 -0
- package/dist/cache/cache.d.ts +31 -0
- package/dist/cache/cache.d.ts.map +1 -0
- package/dist/cache/cache.js +78 -0
- package/dist/cache/cache.js.map +1 -0
- package/dist/cache/index.d.ts +3 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +2 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/core/codec.d.ts +7 -0
- package/dist/core/codec.d.ts.map +1 -0
- package/dist/core/codec.js +24 -0
- package/dist/core/codec.js.map +1 -0
- package/dist/core/errors.d.ts +43 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +84 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/executor.d.ts +28 -0
- package/dist/core/executor.d.ts.map +1 -0
- package/dist/core/executor.js +27 -0
- package/dist/core/executor.js.map +1 -0
- package/dist/core/keys.d.ts +4 -0
- package/dist/core/keys.d.ts.map +1 -0
- package/dist/core/keys.js +15 -0
- package/dist/core/keys.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/ioredis/executor.d.ts +30 -0
- package/dist/ioredis/executor.d.ts.map +1 -0
- package/dist/ioredis/executor.js +52 -0
- package/dist/ioredis/executor.js.map +1 -0
- package/dist/ioredis/index.d.ts +5 -0
- package/dist/ioredis/index.d.ts.map +1 -0
- package/dist/ioredis/index.js +3 -0
- package/dist/ioredis/index.js.map +1 -0
- package/dist/ioredis/pubsub.d.ts +16 -0
- package/dist/ioredis/pubsub.d.ts.map +1 -0
- package/dist/ioredis/pubsub.js +42 -0
- package/dist/ioredis/pubsub.js.map +1 -0
- package/dist/locks/index.d.ts +3 -0
- package/dist/locks/index.d.ts.map +1 -0
- package/dist/locks/index.js +2 -0
- package/dist/locks/index.js.map +1 -0
- package/dist/locks/locks.d.ts +25 -0
- package/dist/locks/locks.d.ts.map +1 -0
- package/dist/locks/locks.js +77 -0
- package/dist/locks/locks.js.map +1 -0
- package/dist/node/client.d.ts +47 -0
- package/dist/node/client.d.ts.map +1 -0
- package/dist/node/client.js +155 -0
- package/dist/node/client.js.map +1 -0
- package/dist/node/errors.d.ts +3 -0
- package/dist/node/errors.d.ts.map +1 -0
- package/dist/node/errors.js +16 -0
- package/dist/node/errors.js.map +1 -0
- package/dist/node/index.d.ts +6 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +4 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/pool.d.ts +31 -0
- package/dist/node/pool.d.ts.map +1 -0
- package/dist/node/pool.js +131 -0
- package/dist/node/pool.js.map +1 -0
- package/dist/node/pubsub.d.ts +16 -0
- package/dist/node/pubsub.d.ts.map +1 -0
- package/dist/node/pubsub.js +41 -0
- package/dist/node/pubsub.js.map +1 -0
- package/dist/pubsub/contracts.d.ts +6 -0
- package/dist/pubsub/contracts.d.ts.map +1 -0
- package/dist/pubsub/contracts.js +2 -0
- package/dist/pubsub/contracts.js.map +1 -0
- package/dist/pubsub/index.d.ts +3 -0
- package/dist/pubsub/index.d.ts.map +1 -0
- package/dist/pubsub/index.js +2 -0
- package/dist/pubsub/index.js.map +1 -0
- package/dist/pubsub/pubsub.d.ts +15 -0
- package/dist/pubsub/pubsub.d.ts.map +1 -0
- package/dist/pubsub/pubsub.js +62 -0
- package/dist/pubsub/pubsub.js.map +1 -0
- package/dist/queue/index.d.ts +3 -0
- package/dist/queue/index.d.ts.map +1 -0
- package/dist/queue/index.js +2 -0
- package/dist/queue/index.js.map +1 -0
- package/dist/queue/queue.d.ts +78 -0
- package/dist/queue/queue.d.ts.map +1 -0
- package/dist/queue/queue.js +260 -0
- package/dist/queue/queue.js.map +1 -0
- package/dist/rate-limit/contracts.d.ts +93 -0
- package/dist/rate-limit/contracts.d.ts.map +1 -0
- package/dist/rate-limit/contracts.js +304 -0
- package/dist/rate-limit/contracts.js.map +1 -0
- package/dist/rate-limit/index.d.ts +3 -0
- package/dist/rate-limit/index.d.ts.map +1 -0
- package/dist/rate-limit/index.js +2 -0
- package/dist/rate-limit/index.js.map +1 -0
- package/dist/testing/fake-executor.d.ts +9 -0
- package/dist/testing/fake-executor.d.ts.map +1 -0
- package/dist/testing/fake-executor.js +15 -0
- package/dist/testing/fake-executor.js.map +1 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +2 -0
- package/dist/testing/index.js.map +1 -0
- package/docs/adapters.md +41 -0
- package/docs/architecture.md +41 -0
- package/docs/errors.md +27 -0
- package/docs/migration.md +21 -0
- package/docs/queues.md +13 -0
- package/docs/rate-limiting.md +23 -0
- package/docs/releasing.md +45 -0
- package/package.json +135 -0
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 mhbdev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# `@mhbdev/redis`
|
|
2
|
+
|
|
3
|
+
Typed Redis infrastructure for Node.js 20+ and Bun. The package provides a managed
|
|
4
|
+
node-redis client, connection pools, atomic rate limiting, typed caching, distributed
|
|
5
|
+
locks, pub/sub, and reliable at-least-once queues.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @mhbdev/redis
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Client and pooling
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createRedisClient, createRedisPool, withRedisClient } from "@mhbdev/redis";
|
|
17
|
+
|
|
18
|
+
await withRedisClient(
|
|
19
|
+
{ url: process.env.REDIS_URL!, commandTimeoutMs: 3_000 },
|
|
20
|
+
async (redis) => {
|
|
21
|
+
await redis.execute(["SET", "health", "ok"]);
|
|
22
|
+
await redis.ping();
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
// Long-lived services can keep the client and close it during shutdown.
|
|
27
|
+
const redis = createRedisClient({ url: process.env.REDIS_URL! });
|
|
28
|
+
await redis.execute(["SET", "key", "value"]); // connects lazily
|
|
29
|
+
|
|
30
|
+
const pool = createRedisPool({
|
|
31
|
+
url: process.env.REDIS_URL!,
|
|
32
|
+
minimum: 1,
|
|
33
|
+
maximum: 10,
|
|
34
|
+
acquireTimeoutMs: 3_000,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
await pool.execute(["SET", "key", "value"]);
|
|
38
|
+
await pool.close();
|
|
39
|
+
await redis.close();
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Use `pool.use(async (client) => ...)` when a sequence must run on one exclusive
|
|
43
|
+
leased node-redis connection. `minimum`, `maximum`, and `acquireTimeoutMs` bound
|
|
44
|
+
resource usage; `withRedisPool` provides the same scoped lifecycle helper as the
|
|
45
|
+
client helper.
|
|
46
|
+
|
|
47
|
+
The high-level APIs use typed transport contracts. Node users can use `raw()` when a
|
|
48
|
+
command or Redis module is intentionally outside this package's focused abstractions.
|
|
49
|
+
Custom adapters can implement `RedisExecutor` for other runtimes.
|
|
50
|
+
|
|
51
|
+
### Adapter choices
|
|
52
|
+
|
|
53
|
+
The feature modules depend on `RedisExecutor`, so an existing client can be swapped
|
|
54
|
+
without changing cache, limiter, lock, or queue code:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import Redis from "ioredis";
|
|
58
|
+
import { createIoredisExecutor } from "@mhbdev/redis/ioredis";
|
|
59
|
+
|
|
60
|
+
const redis = createIoredisExecutor(new Redis(process.env.REDIS_URL!));
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Install `ioredis` only when choosing that adapter:
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
npm install ioredis
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Redis-backed queues and BullMQ share the typed `JobQueue` / `QueueWorker` contracts.
|
|
70
|
+
Use `createBullMqQueueAdapter` with an existing BullMQ `Queue` and an injected worker
|
|
71
|
+
factory when BullMQ's scheduling and retry model is preferred. Install `bullmq` only
|
|
72
|
+
for that integration.
|
|
73
|
+
|
|
74
|
+
## Rate limiting
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { createRateLimiter, fixedWindow } from "@mhbdev/redis/rate-limit";
|
|
78
|
+
import { createRedisClient } from "@mhbdev/redis";
|
|
79
|
+
|
|
80
|
+
const redis = createRedisClient({ url: process.env.REDIS_URL! });
|
|
81
|
+
const limiter = createRateLimiter(fixedWindow(10, 60_000), {
|
|
82
|
+
redis,
|
|
83
|
+
prefix: "api",
|
|
84
|
+
ephemeralCache: true,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const result = await limiter.limit("user-123");
|
|
88
|
+
if (!result.success) {
|
|
89
|
+
console.log(`Retry in ${result.retryAfterMs}ms`);
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Use `slidingWindow(limit, windowMs)` when boundary bursts matter, or
|
|
94
|
+
`tokenBucket(refillRate, intervalMs, maxTokens)` when bursts should be smoothed.
|
|
95
|
+
Rate-limit identifiers are hashed before becoming keys. Redis failures are closed by
|
|
96
|
+
default; use `failureMode: "open"` only when availability is more important than
|
|
97
|
+
strict enforcement.
|
|
98
|
+
|
|
99
|
+
## Cache, locks, pub/sub, and queues
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { createCache, createLockManager, jsonCodec } from "@mhbdev/redis";
|
|
103
|
+
|
|
104
|
+
const cache = createCache<User>({ redis, prefix: "users" }); // JSON by default
|
|
105
|
+
const binaryCache = createCache({ redis, codec: jsonCodec<User>(), prefix: "users" });
|
|
106
|
+
const user = await cache.getOrSet("42", () => loadUser("42"), { ttlMs: 60_000 });
|
|
107
|
+
|
|
108
|
+
const locks = createLockManager({ redis });
|
|
109
|
+
await locks.withLock("user:42", async () => updateUser(user));
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Queues use Redis Streams and consumer groups. Delivery is at-least-once: handlers must
|
|
113
|
+
be idempotent. Queue and pub/sub factories also use JSON by default and accept a custom
|
|
114
|
+
`RedisCodec<T>` when the payload is binary or must be schema-validated. The public
|
|
115
|
+
factory aliases `createRedisCache`, `createRedisQueue`, `createRedisPubSub`, and
|
|
116
|
+
`createRedisRateLimiter` are available for codebases that prefer explicit names.
|
|
117
|
+
|
|
118
|
+
## Typed failures
|
|
119
|
+
|
|
120
|
+
Operational failures are represented by exported error classes and stable `code` values;
|
|
121
|
+
application code never needs to inspect error messages:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
import {
|
|
125
|
+
RedisProtocolError,
|
|
126
|
+
RedisTimeoutError,
|
|
127
|
+
RedisUnavailableError,
|
|
128
|
+
} from "@mhbdev/redis";
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
await redis.ping();
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (error instanceof RedisTimeoutError) return retryLater();
|
|
134
|
+
if (error instanceof RedisUnavailableError) return failOpenForThisFeature();
|
|
135
|
+
if (error instanceof RedisProtocolError) return reportProtocolFailure(error);
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
All causes are preserved on typed errors. Causes are never used for semantic handling by
|
|
141
|
+
message matching, and sensitive command arguments and payloads are not copied into error
|
|
142
|
+
messages.
|
|
143
|
+
|
|
144
|
+
## Development
|
|
145
|
+
|
|
146
|
+
```sh
|
|
147
|
+
npm install
|
|
148
|
+
npm run lint
|
|
149
|
+
npm run typecheck
|
|
150
|
+
npm test
|
|
151
|
+
npm run build
|
|
152
|
+
npm run pack:check
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Live Redis tests are separate from the unit suite. Run them with a local Redis service:
|
|
156
|
+
|
|
157
|
+
```sh
|
|
158
|
+
npm run test:integration
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Documentation
|
|
162
|
+
|
|
163
|
+
- [Architecture](docs/architecture.md)
|
|
164
|
+
- [Rate limiting](docs/rate-limiting.md)
|
|
165
|
+
- [Queues](docs/queues.md)
|
|
166
|
+
- [Adapter bridges](docs/adapters.md)
|
|
167
|
+
- [Errors and failure handling](docs/errors.md)
|
|
168
|
+
- [Releasing to npm](docs/releasing.md)
|
|
169
|
+
- [Migration from `@dordoone/redis-runtime`](docs/migration.md)
|
|
170
|
+
|
|
171
|
+
## License
|
|
172
|
+
|
|
173
|
+
MIT © 2026 mhbdev
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type RedisCodec } from "../core/codec.js";
|
|
2
|
+
import type { EnqueueOptions, QueueJob, QueueWorker, QueueWorkerOptions } from "../queue/queue.js";
|
|
3
|
+
export interface BullMqQueueLike {
|
|
4
|
+
add(name: string, data: unknown, options?: Readonly<{
|
|
5
|
+
delay?: number;
|
|
6
|
+
jobId?: string;
|
|
7
|
+
attempts?: number;
|
|
8
|
+
}>): Promise<{
|
|
9
|
+
id?: string | number;
|
|
10
|
+
}>;
|
|
11
|
+
close?(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export interface BullMqJobLike {
|
|
14
|
+
id?: string | number;
|
|
15
|
+
data: unknown;
|
|
16
|
+
}
|
|
17
|
+
export interface BullMqWorkerLike {
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export type BullMqWorkerFactory = (name: string, processor: (job: BullMqJobLike) => Promise<void>, options?: QueueWorkerOptions) => BullMqWorkerLike;
|
|
21
|
+
export declare class BullMqQueueAdapter<T> {
|
|
22
|
+
private readonly queue;
|
|
23
|
+
private readonly options;
|
|
24
|
+
private readonly jobName;
|
|
25
|
+
constructor(queue: BullMqQueueLike, options: Readonly<{
|
|
26
|
+
codec?: RedisCodec<T>;
|
|
27
|
+
jobName?: string;
|
|
28
|
+
maxAttempts?: number;
|
|
29
|
+
workerFactory?: BullMqWorkerFactory;
|
|
30
|
+
}>);
|
|
31
|
+
private readonly codec;
|
|
32
|
+
enqueue(data: T, enqueueOptions?: EnqueueOptions): Promise<string>;
|
|
33
|
+
worker(handler: (job: QueueJob<T>) => Promise<void>, workerOptions?: QueueWorkerOptions): BullMqWorkerAdapter;
|
|
34
|
+
close(): Promise<void>;
|
|
35
|
+
private decodeJob;
|
|
36
|
+
}
|
|
37
|
+
export declare class BullMqWorkerAdapter implements QueueWorker {
|
|
38
|
+
private readonly worker;
|
|
39
|
+
constructor(worker: BullMqWorkerLike);
|
|
40
|
+
start(): Promise<void>;
|
|
41
|
+
close(): Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
export declare function createBullMqQueueAdapter<T>(queue: BullMqQueueLike, options: Readonly<{
|
|
44
|
+
codec?: RedisCodec<T>;
|
|
45
|
+
jobName?: string;
|
|
46
|
+
maxAttempts?: number;
|
|
47
|
+
workerFactory?: BullMqWorkerFactory;
|
|
48
|
+
}>): BullMqQueueAdapter<T>;
|
|
49
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/bullmq/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9D,OAAO,KAAK,EACX,cAAc,EACd,QAAQ,EACR,WAAW,EACX,kBAAkB,EAClB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,eAAe;IAC/B,GAAG,CACF,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE,QAAQ,CAAC;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GACvE,OAAO,CAAC;QAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;IACrC,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC7B,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,IAAI,EAAE,OAAO,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAChC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,MAAM,MAAM,mBAAmB,GAAG,CACjC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,EAChD,OAAO,CAAC,EAAE,kBAAkB,KACxB,gBAAgB,CAAC;AAEtB,qBAAa,kBAAkB,CAAC,CAAC;IAI/B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC,YACkB,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,QAAQ,CAAC;QAClC,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;QACtB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,aAAa,CAAC,EAAE,mBAAmB,CAAC;KACpC,CAAC,EAIF;IAED,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IAEhC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,cAAc,GAAE,cAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAO3E;IAED,MAAM,CACL,OAAO,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,EAC5C,aAAa,GAAE,kBAAuB,GACpC,mBAAmB,CAWrB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3B;IAED,OAAO,CAAC,SAAS;CASjB;AAED,qBAAa,mBAAoB,YAAW,WAAW;IAC1C,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAnC,YAA6B,MAAM,EAAE,gBAAgB,EAAI;IAEzD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAErB;IAED,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAErB;CACD;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EACzC,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,QAAQ,CAAC;IACjB,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACpC,CAAC,GACA,kBAAkB,CAAC,CAAC,CAAC,CAEvB"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { jsonCodec } from "../core/codec.js";
|
|
2
|
+
import { RedisConfigurationError } from "../core/errors.js";
|
|
3
|
+
export class BullMqQueueAdapter {
|
|
4
|
+
queue;
|
|
5
|
+
options;
|
|
6
|
+
jobName;
|
|
7
|
+
constructor(queue, options) {
|
|
8
|
+
this.queue = queue;
|
|
9
|
+
this.options = options;
|
|
10
|
+
this.jobName = options.jobName ?? "job";
|
|
11
|
+
this.codec = options.codec ?? jsonCodec();
|
|
12
|
+
}
|
|
13
|
+
codec;
|
|
14
|
+
async enqueue(data, enqueueOptions = {}) {
|
|
15
|
+
const job = await this.queue.add(this.jobName, this.codec.encode(data), {
|
|
16
|
+
delay: enqueueOptions.delayMs,
|
|
17
|
+
jobId: enqueueOptions.jobId,
|
|
18
|
+
attempts: this.options.maxAttempts,
|
|
19
|
+
});
|
|
20
|
+
return String(job.id ?? enqueueOptions.jobId ?? crypto.randomUUID());
|
|
21
|
+
}
|
|
22
|
+
worker(handler, workerOptions = {}) {
|
|
23
|
+
if (!this.options.workerFactory)
|
|
24
|
+
throw new RedisConfigurationError("A BullMQ workerFactory is required to create workers");
|
|
25
|
+
const worker = this.options.workerFactory(this.jobName, async (job) => handler(this.decodeJob(job)), workerOptions);
|
|
26
|
+
return new BullMqWorkerAdapter(worker);
|
|
27
|
+
}
|
|
28
|
+
async close() {
|
|
29
|
+
await this.queue.close?.();
|
|
30
|
+
}
|
|
31
|
+
decodeJob(job) {
|
|
32
|
+
const encoded = typeof job.data === "string" ? job.data : JSON.stringify(job.data);
|
|
33
|
+
return {
|
|
34
|
+
id: String(job.id ?? crypto.randomUUID()),
|
|
35
|
+
data: this.codec.decode(encoded),
|
|
36
|
+
attempts: 0,
|
|
37
|
+
enqueuedAt: Date.now(),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export class BullMqWorkerAdapter {
|
|
42
|
+
worker;
|
|
43
|
+
constructor(worker) {
|
|
44
|
+
this.worker = worker;
|
|
45
|
+
}
|
|
46
|
+
start() {
|
|
47
|
+
return Promise.resolve();
|
|
48
|
+
}
|
|
49
|
+
close() {
|
|
50
|
+
return this.worker.close();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function createBullMqQueueAdapter(queue, options) {
|
|
54
|
+
return new BullMqQueueAdapter(queue, options);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/bullmq/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAmB,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAgC5D,MAAM,OAAO,kBAAkB;IAIZ,KAAK;IACL,OAAO;IAJR,OAAO,CAAS;IAEjC,YACkB,KAAsB,EACtB,OAKf;qBANe,KAAK;uBACL,OAAO;QAOxB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC;QACxC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,SAAS,EAAK,CAAC;IAC9C,CAAC;IAEgB,KAAK,CAAgB;IAEtC,KAAK,CAAC,OAAO,CAAC,IAAO,EAAE,cAAc,GAAmB,EAAE;QACzD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;YACvE,KAAK,EAAE,cAAc,CAAC,OAAO;YAC7B,KAAK,EAAE,cAAc,CAAC,KAAK;YAC3B,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW;SAClC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,cAAc,CAAC,KAAK,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,CACL,OAA4C,EAC5C,aAAa,GAAuB,EAAE;QAEtC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa;YAC9B,MAAM,IAAI,uBAAuB,CAChC,sDAAsD,CACtD,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CACxC,IAAI,CAAC,OAAO,EACZ,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAC3C,aAAa,CACb,CAAC;QACF,OAAO,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,KAAK;QACV,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;IAC5B,CAAC;IAEO,SAAS,CAAC,GAAkB;QACnC,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACnF,OAAO;YACN,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACzC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC;YAChC,QAAQ,EAAE,CAAC;YACX,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;IACH,CAAC;CACD;AAED,MAAM,OAAO,mBAAmB;IACF,MAAM;IAAnC,YAA6B,MAAwB;sBAAxB,MAAM;IAAqB,CAAC;IAEzD,KAAK;QACJ,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC1B,CAAC;IAED,KAAK;QACJ,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC;CACD;AAED,MAAM,UAAU,wBAAwB,CACvC,KAAsB,EACtB,OAKE;IAEF,OAAO,IAAI,kBAAkB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bullmq/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,gBAAgB,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,GACxB,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bullmq/index.ts"],"names":[],"mappings":"AAMA,OAAO,EACN,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,GACxB,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type RedisCodec } from "../core/codec.js";
|
|
2
|
+
import type { RedisExecutor } from "../core/executor.js";
|
|
3
|
+
export type CacheSetOptions = Readonly<{
|
|
4
|
+
ttlMs?: number;
|
|
5
|
+
onlyIfAbsent?: boolean;
|
|
6
|
+
onlyIfPresent?: boolean;
|
|
7
|
+
}>;
|
|
8
|
+
export type RedisCacheOptions<T> = Readonly<{
|
|
9
|
+
redis: RedisExecutor;
|
|
10
|
+
/** JSON is used by default. Provide a codec for binary or schema-validated values. */
|
|
11
|
+
codec?: RedisCodec<T>;
|
|
12
|
+
prefix?: string;
|
|
13
|
+
maxKeyLength?: number;
|
|
14
|
+
}>;
|
|
15
|
+
export declare class RedisCache<T> {
|
|
16
|
+
private readonly options;
|
|
17
|
+
private readonly prefix;
|
|
18
|
+
private readonly codec;
|
|
19
|
+
private readonly pending;
|
|
20
|
+
constructor(options: RedisCacheOptions<T>);
|
|
21
|
+
get(key: string): Promise<T | undefined>;
|
|
22
|
+
set(key: string, value: T, options?: CacheSetOptions): Promise<boolean>;
|
|
23
|
+
delete(key: string): Promise<boolean>;
|
|
24
|
+
exists(key: string): Promise<boolean>;
|
|
25
|
+
getOrSet(key: string, factory: () => Promise<T>, options?: CacheSetOptions): Promise<T>;
|
|
26
|
+
private key;
|
|
27
|
+
}
|
|
28
|
+
export declare function createCache<T>(options: RedisCacheOptions<T>): RedisCache<T>;
|
|
29
|
+
/** A discoverable alias for applications that prefer Redis-prefixed factory names. */
|
|
30
|
+
export declare const createRedisCache: typeof createCache;
|
|
31
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/cache/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC,CAAC;AAEH,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI,QAAQ,CAAC;IAC3C,KAAK,EAAE,aAAa,CAAC;IACrB,sFAAsF;IACtF,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC,CAAC;AAEH,qBAAa,UAAU,CAAC,CAAC;IAKZ,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6C;IAErE,YAA6B,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAKzD;IAEK,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAS7C;IAEK,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,CAYhF;IAEK,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE1C;IAEK,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE1C;IAEK,QAAQ,CACb,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACzB,OAAO,GAAE,eAAoB,GAC3B,OAAO,CAAC,CAAC,CAAC,CAaZ;IAED,OAAO,CAAC,GAAG;CASX;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAE3E;AAED,sFAAsF;AACtF,eAAO,MAAM,gBAAgB,oBAAc,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { jsonCodec } from "../core/codec.js";
|
|
2
|
+
import { RedisConfigurationError, RedisSerializationError } from "../core/errors.js";
|
|
3
|
+
export class RedisCache {
|
|
4
|
+
options;
|
|
5
|
+
prefix;
|
|
6
|
+
codec;
|
|
7
|
+
pending = new Map();
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.options = options;
|
|
10
|
+
this.prefix = options.prefix ?? "mhbdev:cache";
|
|
11
|
+
this.codec = options.codec ?? jsonCodec();
|
|
12
|
+
if (!this.prefix.trim())
|
|
13
|
+
throw new RedisConfigurationError("Cache prefix is required");
|
|
14
|
+
}
|
|
15
|
+
async get(key) {
|
|
16
|
+
const value = await this.options.redis.execute(["GET", this.key(key)]);
|
|
17
|
+
if (value === null)
|
|
18
|
+
return undefined;
|
|
19
|
+
try {
|
|
20
|
+
return this.codec.decode(value);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (error instanceof RedisSerializationError)
|
|
24
|
+
throw error;
|
|
25
|
+
throw new RedisSerializationError("Unable to decode cached value", error);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async set(key, value, options = {}) {
|
|
29
|
+
const encoded = this.codec.encode(value);
|
|
30
|
+
const command = ["SET", this.key(key), encoded];
|
|
31
|
+
if (options.ttlMs !== undefined) {
|
|
32
|
+
if (!Number.isSafeInteger(options.ttlMs) || options.ttlMs <= 0)
|
|
33
|
+
throw new RedisConfigurationError("Cache TTL must be positive");
|
|
34
|
+
command.push("PX", String(options.ttlMs));
|
|
35
|
+
}
|
|
36
|
+
if (options.onlyIfAbsent)
|
|
37
|
+
command.push("NX");
|
|
38
|
+
if (options.onlyIfPresent)
|
|
39
|
+
command.push("XX");
|
|
40
|
+
const result = await this.options.redis.execute(command);
|
|
41
|
+
return result === "OK";
|
|
42
|
+
}
|
|
43
|
+
async delete(key) {
|
|
44
|
+
return (await this.options.redis.execute(["DEL", this.key(key)])) > 0;
|
|
45
|
+
}
|
|
46
|
+
async exists(key) {
|
|
47
|
+
return (await this.options.redis.execute(["EXISTS", this.key(key)])) > 0;
|
|
48
|
+
}
|
|
49
|
+
async getOrSet(key, factory, options = {}) {
|
|
50
|
+
const existing = await this.get(key);
|
|
51
|
+
if (existing !== undefined)
|
|
52
|
+
return existing;
|
|
53
|
+
const pending = this.pending.get(key);
|
|
54
|
+
if (pending)
|
|
55
|
+
return (await pending);
|
|
56
|
+
const operation = factory()
|
|
57
|
+
.then(async (value) => {
|
|
58
|
+
await this.set(key, value, options);
|
|
59
|
+
return value;
|
|
60
|
+
})
|
|
61
|
+
.finally(() => this.pending.delete(key));
|
|
62
|
+
this.pending.set(key, operation);
|
|
63
|
+
return operation;
|
|
64
|
+
}
|
|
65
|
+
key(key) {
|
|
66
|
+
if (!key.trim() ||
|
|
67
|
+
key.length > (this.options.maxKeyLength ?? 512) ||
|
|
68
|
+
key.includes("\n"))
|
|
69
|
+
throw new RedisConfigurationError("Cache key is invalid");
|
|
70
|
+
return `${this.prefix}:${key}`;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export function createCache(options) {
|
|
74
|
+
return new RedisCache(options);
|
|
75
|
+
}
|
|
76
|
+
/** A discoverable alias for applications that prefer Redis-prefixed factory names. */
|
|
77
|
+
export const createRedisCache = createCache;
|
|
78
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/cache/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../src/core/codec.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU,CAAC,CAAC;IAC5B,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC;IACzB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC;CACzB;AAED,eAAO,MAAM,SAAS,GAAI,CAAC,OAAK,UAAU,CAAC,CAAC,CAe1C,CAAC;AAEH,eAAO,MAAM,WAAW,EAAE,UAAU,CAAC,MAAM,CAG1C,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { RedisSerializationError } from "./errors.js";
|
|
2
|
+
export const jsonCodec = () => ({
|
|
3
|
+
encode(value) {
|
|
4
|
+
try {
|
|
5
|
+
return JSON.stringify(value);
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
throw new RedisSerializationError("Unable to encode Redis value", error);
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
decode(value) {
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(value);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
throw new RedisSerializationError("Unable to decode Redis value", error);
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
export const stringCodec = {
|
|
21
|
+
encode: (value) => value,
|
|
22
|
+
decode: (value) => value,
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=codec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec.js","sourceRoot":"","sources":["../../src/core/codec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAOtD,MAAM,CAAC,MAAM,SAAS,GAAG,GAAqB,EAAE,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK;QACX,IAAI,CAAC;YACJ,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,uBAAuB,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;QAC1E,CAAC;IACF,CAAC;IACD,MAAM,CAAC,KAAK;QACX,IAAI,CAAC;YACJ,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAM,CAAC;QAC/B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,uBAAuB,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;QAC1E,CAAC;IACF,CAAC;CACD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,WAAW,GAAuB;IAC9C,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK;IACxB,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK;CACxB,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type RedisErrorCode = "REDIS_CONFIGURATION_ERROR" | "REDIS_CONNECTION_ERROR" | "REDIS_TIMEOUT" | "REDIS_ABORTED" | "REDIS_UNAVAILABLE" | "REDIS_CLIENT_STATE" | "REDIS_PROTOCOL_ERROR" | "REDIS_SERIALIZATION_ERROR" | "REDIS_LOCK_ERROR" | "REDIS_QUEUE_ERROR";
|
|
2
|
+
export declare class RedisError extends Error {
|
|
3
|
+
readonly code: RedisErrorCode;
|
|
4
|
+
readonly isOperational: boolean;
|
|
5
|
+
readonly cause: unknown;
|
|
6
|
+
constructor(code: RedisErrorCode, message: string, cause?: unknown, isOperational?: boolean);
|
|
7
|
+
}
|
|
8
|
+
export declare class RedisConfigurationError extends RedisError {
|
|
9
|
+
constructor(message: string, cause?: unknown);
|
|
10
|
+
}
|
|
11
|
+
export declare class RedisConnectionError extends RedisError {
|
|
12
|
+
constructor(message?: string, cause?: unknown);
|
|
13
|
+
}
|
|
14
|
+
export declare class RedisTimeoutError extends RedisError {
|
|
15
|
+
constructor(message?: string, cause?: unknown);
|
|
16
|
+
}
|
|
17
|
+
export declare class RedisRequestAbortedError extends RedisError {
|
|
18
|
+
constructor(cause?: unknown);
|
|
19
|
+
}
|
|
20
|
+
export declare class RedisUnavailableError extends RedisError {
|
|
21
|
+
constructor(cause?: unknown);
|
|
22
|
+
}
|
|
23
|
+
export declare class RedisClientStateError extends RedisError {
|
|
24
|
+
constructor(message?: string);
|
|
25
|
+
}
|
|
26
|
+
export declare class RedisProtocolError extends RedisError {
|
|
27
|
+
constructor(message?: string, cause?: unknown);
|
|
28
|
+
}
|
|
29
|
+
export declare class RedisSerializationError extends RedisError {
|
|
30
|
+
constructor(message?: string, cause?: unknown);
|
|
31
|
+
}
|
|
32
|
+
export declare class RedisLockError extends RedisError {
|
|
33
|
+
constructor(message: string, cause?: unknown);
|
|
34
|
+
}
|
|
35
|
+
export declare class RedisQueueError extends RedisError {
|
|
36
|
+
constructor(message: string, cause?: unknown);
|
|
37
|
+
}
|
|
38
|
+
export declare function asError(error: unknown): Error;
|
|
39
|
+
export declare function isRedisError(error: unknown): error is RedisError;
|
|
40
|
+
export declare function isRedisErrorCode<Code extends RedisErrorCode>(error: unknown, code: Code): error is RedisError & {
|
|
41
|
+
readonly code: Code;
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GACvB,2BAA2B,GAC3B,wBAAwB,GACxB,eAAe,GACf,eAAe,GACf,mBAAmB,GACnB,oBAAoB,GACpB,sBAAsB,GACtB,2BAA2B,GAC3B,kBAAkB,GAClB,mBAAmB,CAAC;AAEvB,qBAAa,UAAW,SAAQ,KAAK;IAKnC,QAAQ,CAAC,IAAI,EAAE,cAAc;IAJ9B,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,SAAkB,KAAK,EAAE,OAAO,CAAC;IAEjC,YACU,IAAI,EAAE,cAAc,EAC7B,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,OAAO,EACf,aAAa,UAAO,EAMpB;CACD;AAED,qBAAa,uBAAwB,SAAQ,UAAU;IACtD,YAAY,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAG3C;CACD;AAED,qBAAa,oBAAqB,SAAQ,UAAU;IACnD,YAAY,OAAO,SAA4B,EAAE,KAAK,CAAC,EAAE,OAAO,EAG/D;CACD;AAED,qBAAa,iBAAkB,SAAQ,UAAU;IAChD,YAAY,OAAO,SAA4B,EAAE,KAAK,CAAC,EAAE,OAAO,EAG/D;CACD;AAED,qBAAa,wBAAyB,SAAQ,UAAU;IACvD,YAAY,KAAK,CAAC,EAAE,OAAO,EAG1B;CACD;AAED,qBAAa,qBAAsB,SAAQ,UAAU;IACpD,YAAY,KAAK,CAAC,EAAE,OAAO,EAG1B;CACD;AAED,qBAAa,qBAAsB,SAAQ,UAAU;IACpD,YAAY,OAAO,SAAsC,EAGxD;CACD;AAED,qBAAa,kBAAmB,SAAQ,UAAU;IACjD,YAAY,OAAO,SAAuC,EAAE,KAAK,CAAC,EAAE,OAAO,EAG1E;CACD;AAED,qBAAa,uBAAwB,SAAQ,UAAU;IACtD,YAAY,OAAO,SAAqC,EAAE,KAAK,CAAC,EAAE,OAAO,EAGxE;CACD;AAED,qBAAa,cAAe,SAAQ,UAAU;IAC7C,YAAY,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAG3C;CACD;AAED,qBAAa,eAAgB,SAAQ,UAAU;IAC9C,YAAY,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAG3C;CACD;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAI7C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAEhE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,SAAS,cAAc,EAC3D,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,IAAI,GACR,KAAK,IAAI,UAAU,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;CAAE,CAE/C"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export class RedisError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
isOperational;
|
|
4
|
+
cause;
|
|
5
|
+
constructor(code, message, cause, isOperational = true) {
|
|
6
|
+
super(message, { cause });
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.name = "RedisError";
|
|
9
|
+
this.isOperational = isOperational;
|
|
10
|
+
this.cause = cause;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class RedisConfigurationError extends RedisError {
|
|
14
|
+
constructor(message, cause) {
|
|
15
|
+
super("REDIS_CONFIGURATION_ERROR", message, cause);
|
|
16
|
+
this.name = "RedisConfigurationError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class RedisConnectionError extends RedisError {
|
|
20
|
+
constructor(message = "Redis connection failed", cause) {
|
|
21
|
+
super("REDIS_CONNECTION_ERROR", message, cause);
|
|
22
|
+
this.name = "RedisConnectionError";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export class RedisTimeoutError extends RedisError {
|
|
26
|
+
constructor(message = "Redis command timed out", cause) {
|
|
27
|
+
super("REDIS_TIMEOUT", message, cause);
|
|
28
|
+
this.name = "RedisTimeoutError";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export class RedisRequestAbortedError extends RedisError {
|
|
32
|
+
constructor(cause) {
|
|
33
|
+
super("REDIS_ABORTED", "The Redis operation was aborted", cause);
|
|
34
|
+
this.name = "RedisRequestAbortedError";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export class RedisUnavailableError extends RedisError {
|
|
38
|
+
constructor(cause) {
|
|
39
|
+
super("REDIS_UNAVAILABLE", "The Redis service is unavailable", cause);
|
|
40
|
+
this.name = "RedisUnavailableError";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export class RedisClientStateError extends RedisError {
|
|
44
|
+
constructor(message = "The Redis client is not available") {
|
|
45
|
+
super("REDIS_CLIENT_STATE", message);
|
|
46
|
+
this.name = "RedisClientStateError";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export class RedisProtocolError extends RedisError {
|
|
50
|
+
constructor(message = "Redis returned an invalid response", cause) {
|
|
51
|
+
super("REDIS_PROTOCOL_ERROR", message, cause);
|
|
52
|
+
this.name = "RedisProtocolError";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export class RedisSerializationError extends RedisError {
|
|
56
|
+
constructor(message = "Redis value serialization failed", cause) {
|
|
57
|
+
super("REDIS_SERIALIZATION_ERROR", message, cause);
|
|
58
|
+
this.name = "RedisSerializationError";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export class RedisLockError extends RedisError {
|
|
62
|
+
constructor(message, cause) {
|
|
63
|
+
super("REDIS_LOCK_ERROR", message, cause);
|
|
64
|
+
this.name = "RedisLockError";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export class RedisQueueError extends RedisError {
|
|
68
|
+
constructor(message, cause) {
|
|
69
|
+
super("REDIS_QUEUE_ERROR", message, cause);
|
|
70
|
+
this.name = "RedisQueueError";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export function asError(error) {
|
|
74
|
+
return error instanceof Error
|
|
75
|
+
? error
|
|
76
|
+
: new Error("Unknown Redis failure", { cause: error });
|
|
77
|
+
}
|
|
78
|
+
export function isRedisError(error) {
|
|
79
|
+
return error instanceof RedisError;
|
|
80
|
+
}
|
|
81
|
+
export function isRedisErrorCode(error, code) {
|
|
82
|
+
return error instanceof RedisError && error.code === code;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=errors.js.map
|