@tsed/adapters-redis 8.28.8 → 8.29.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.
@@ -1,22 +1,20 @@
1
1
  import { __decorate, __metadata, __param } from "tslib";
2
- import { Configuration, Inject, Opts } from "@tsed/di";
3
- import { IOREDIS_CONNECTIONS } from "@tsed/ioredis";
2
+ import { Opts } from "@tsed/di";
4
3
  import { RedisAdapter } from "./RedisAdapter.js";
5
4
  const GRANTABLE = new Set(["AccessToken", "AuthorizationCode", "RefreshToken", "DeviceCode", "BackchannelAuthenticationRequest"]);
6
5
  const CONSUMABLE = new Set(["AuthorizationCode", "RefreshToken", "DeviceCode", "BackchannelAuthenticationRequest"]);
7
- function grantKeyFor(id) {
8
- return `$oidc:grant:${id}`;
9
- }
10
- function userCodeKeyFor(userCode) {
11
- return `$oidc:userCode:${userCode}`;
12
- }
13
- function uidKeyFor(uid) {
14
- return `$oidc:uid:${uid}`;
15
- }
16
6
  let OIDCRedisAdapter = class OIDCRedisAdapter extends RedisAdapter {
17
- constructor(options, connections, configuration) {
18
- super(options, connections, configuration);
19
- this.configuration = configuration;
7
+ grantKeyFor(id) {
8
+ return this.prefix(`$oidc:grant:${id}`);
9
+ }
10
+ userCodeKeyFor(userCode) {
11
+ return this.prefix(`$oidc:userCode:${userCode}`);
12
+ }
13
+ uidKeyFor(uid) {
14
+ return this.prefix(`$oidc:uid:${uid}`);
15
+ }
16
+ constructor(options) {
17
+ super(options);
20
18
  this.useHash = CONSUMABLE.has(this.collectionName);
21
19
  this.isGrantable = GRANTABLE.has(this.collectionName);
22
20
  this.hooks.on("insert", this.onInsert.bind(this));
@@ -25,55 +23,53 @@ let OIDCRedisAdapter = class OIDCRedisAdapter extends RedisAdapter {
25
23
  const id = payload._id;
26
24
  const key = this.key(id);
27
25
  if (this.isGrantable && payload.grantId) {
28
- const grantKey = grantKeyFor(payload.grantId);
29
- multi.rpush(grantKey, key);
26
+ const grantKey = this.grantKeyFor(payload.grantId);
27
+ this.callCmd(multi, ["rPush", "rpush"], grantKey, key);
30
28
  // if you're seeing grant key lists growing out of acceptable proportions consider using LTRIM
31
29
  // here to trim the list to an appropriate length
32
- const ttl = await this.db.ttl(grantKey);
30
+ const ttl = await this.callCmd(this.db, ["ttl"], grantKey);
33
31
  if (expiresIn && expiresIn > ttl) {
34
- multi.expire(grantKey, expiresIn);
32
+ this.callCmd(multi, ["expire"], grantKey, expiresIn);
35
33
  }
36
34
  }
37
35
  if (payload.userCode) {
38
- const userCodeKey = userCodeKeyFor(payload.userCode);
39
- multi.set(userCodeKey, id);
40
- expiresIn && multi.expire(userCodeKey, expiresIn);
36
+ const userCodeKey = this.userCodeKeyFor(payload.userCode);
37
+ this.callCmd(multi, ["set"], userCodeKey, id);
38
+ expiresIn && this.callCmd(multi, ["expire"], userCodeKey, expiresIn);
41
39
  }
42
40
  if (payload.uid) {
43
- const uidKey = uidKeyFor(payload.uid);
44
- multi.set(uidKey, id);
45
- expiresIn && multi.expire(uidKey, expiresIn);
41
+ const uidKey = this.uidKeyFor(payload.uid);
42
+ this.callCmd(multi, ["set"], uidKey, id);
43
+ expiresIn && this.callCmd(multi, ["expire"], uidKey, expiresIn);
46
44
  }
47
45
  return multi;
48
46
  }
49
47
  async findByUid(uid) {
50
- const id = await this.db.get(uidKeyFor(uid));
48
+ const id = await this.callCmd(this.db, ["get"], this.uidKeyFor(uid));
51
49
  return id && this.findById(id);
52
50
  }
53
51
  async findByUserCode(userCode) {
54
- const id = await this.db.get(userCodeKeyFor(userCode));
52
+ const id = await this.callCmd(this.db, ["get"], this.userCodeKeyFor(userCode));
55
53
  return id && this.findById(id);
56
54
  }
57
55
  async destroy(id) {
58
56
  const key = this.key(id);
59
- await this.db.del(key);
57
+ await this.callCmd(this.db, ["del"], key);
60
58
  }
61
59
  async revokeByGrantId(grantId) {
62
60
  const multi = this.db.multi();
63
- const key = grantKeyFor(grantId);
64
- const tokens = await this.db.lrange(key, 0, -1);
65
- tokens.forEach((token) => multi.del(token));
66
- multi.del(grantKeyFor(grantId));
67
- await multi.exec();
61
+ const key = this.grantKeyFor(grantId);
62
+ const tokens = await this.callCmd(this.db, ["lRange", "lrange"], key, 0, -1);
63
+ tokens.forEach((token) => this.callCmd(multi, ["del"], token));
64
+ this.callCmd(multi, ["del"], this.grantKeyFor(grantId));
65
+ await this.callCmd(multi, ["exec"]);
68
66
  }
69
67
  async consume(id) {
70
- await this.db.hset(this.key(id), "consumed", Math.floor(Date.now() / 1000));
68
+ await this.callCmd(this.db, ["hSet", "hset"], this.key(id), "consumed", Math.floor(Date.now() / 1000));
71
69
  }
72
70
  };
73
71
  OIDCRedisAdapter = __decorate([
74
72
  __param(0, Opts),
75
- __param(1, Inject(IOREDIS_CONNECTIONS)),
76
- __param(2, Configuration()),
77
- __metadata("design:paramtypes", [Object, Array, Object])
73
+ __metadata("design:paramtypes", [Object])
78
74
  ], OIDCRedisAdapter);
79
75
  export { OIDCRedisAdapter };
@@ -1,29 +1,27 @@
1
1
  import { __decorate, __metadata, __param } from "tslib";
2
2
  import { Adapter } from "@tsed/adapters";
3
3
  import { cleanObject, isObject, isString } from "@tsed/core";
4
- import { Configuration, Inject, Opts } from "@tsed/di";
4
+ import { injectMany, Opts } from "@tsed/di";
5
5
  import { Hooks } from "@tsed/hooks";
6
- import { IOREDIS_CONNECTIONS } from "@tsed/ioredis";
6
+ import { REDIS_CONNECTIONS } from "@tsed/redis";
7
7
  import { v4 as uuid } from "uuid";
8
- const getId = (key) => key.split(":")[2];
9
- const flatKeys = (keys) => {
10
- return keys.flatMap(([, result]) => result).filter(Boolean);
11
- };
12
8
  let RedisAdapter = class RedisAdapter extends Adapter {
13
- constructor(options, connections, configuration) {
14
- super(options, configuration);
15
- this.configuration = configuration;
9
+ constructor(options) {
10
+ super(options);
16
11
  this.hooks = new Hooks();
17
12
  this.useHash = false;
18
13
  this.useHash = Boolean(options.useHash);
19
14
  this.connectionName = options.connectionName || "default";
20
- this.connection = connections.find((connection) => connection.name === this.connectionName); // || connections[0];
15
+ this.keyPrefix = options.keyPrefix || "";
16
+ this.connection = injectMany(REDIS_CONNECTIONS).find((connection) => {
17
+ return connection.name === this.connectionName;
18
+ }); // || connections[0];
21
19
  }
22
20
  get db() {
23
21
  return this.connection;
24
22
  }
25
23
  key(id) {
26
- return `${this.collectionName}:${id}`;
24
+ return this.prefix(`${this.collectionName}:${id}`);
27
25
  }
28
26
  create(payload, expiresAt) {
29
27
  delete payload._id;
@@ -60,14 +58,16 @@ let RedisAdapter = class RedisAdapter extends Adapter {
60
58
  if (foundKeys.length < keys.length) {
61
59
  return undefined;
62
60
  }
63
- const getId = (key) => key.split(":")[2];
64
- const id = getId(foundKeys[0]);
65
- foundKeys = foundKeys.filter((key) => id === getId(key));
61
+ const id = this.extractIdFromIndexedKey(foundKeys[0]);
62
+ if (!id) {
63
+ return undefined;
64
+ }
65
+ foundKeys = foundKeys.filter((key) => id === this.extractIdFromIndexedKey(key));
66
66
  return keys.length === foundKeys.length ? this.findById(id) : undefined;
67
67
  }
68
68
  async findById(_id) {
69
69
  const key = this.key(_id);
70
- const item = this.useHash ? await this.db.hgetall(key) : await this.db.get(key);
70
+ const item = this.useHash ? await this.callCmd(this.db, ["hGetAll", "hgetall"], key) : await this.callCmd(this.db, ["get"], key);
71
71
  // hgetall returns an empty object when there are no results, so we need to check for that.
72
72
  if (!item || (this.useHash && isObject(item) && Object.keys(item).length === 0)) {
73
73
  return undefined;
@@ -98,7 +98,7 @@ let RedisAdapter = class RedisAdapter extends Adapter {
98
98
  if (item) {
99
99
  const id = this.key(item._id);
100
100
  const indexIds = await this.getAllIndex(item._id);
101
- await this.db.del([id, ...indexIds]);
101
+ await this.callCmd(this.db, ["del"], id, ...indexIds);
102
102
  return this.deserialize(item);
103
103
  }
104
104
  }
@@ -107,29 +107,43 @@ let RedisAdapter = class RedisAdapter extends Adapter {
107
107
  }
108
108
  async deleteMany(predicate) {
109
109
  const items = await this.findAll(predicate);
110
- const pipeline = this.db.pipeline();
111
110
  const ids = [];
112
111
  const results = items.map((item) => {
113
112
  ids.push(this.key(item._id));
114
- this.getAllIndex(item._id, pipeline);
115
113
  return this.deserialize(item);
116
114
  });
117
- const keys = await pipeline.exec();
118
- this.db.del([...ids, ...flatKeys(keys)]);
115
+ const indexesByItem = await Promise.all(items.map((item) => this.getAllIndex(item._id)));
116
+ const indexKeys = indexesByItem.flat();
117
+ if (ids.length || indexKeys.length) {
118
+ await this.callCmd(this.db, ["del"], ...ids, ...indexKeys);
119
+ }
119
120
  return results;
120
121
  }
122
+ prefix(key) {
123
+ return this.keyPrefix ? `${this.keyPrefix}:${key}` : key;
124
+ }
125
+ callCmd(target, methods, ...args) {
126
+ const method = methods.map((name) => target?.[name]).find((fn) => typeof fn === "function");
127
+ if (!method) {
128
+ throw new Error(`Redis command not available: ${methods.join(" or ")}`);
129
+ }
130
+ return method.call(target, ...args);
131
+ }
132
+ extractIdFromIndexedKey(key) {
133
+ const parts = key.split(":");
134
+ const collectionIndex = parts.lastIndexOf(this.collectionName);
135
+ return collectionIndex > -1 ? parts[collectionIndex + 1] : undefined;
136
+ }
121
137
  async findKeys(props) {
122
138
  const keys = Object.keys(props);
123
- const pipeline = this.db.pipeline();
124
- this.indexes
139
+ const patterns = this.indexes
125
140
  .filter(({ propertyKey }) => keys.includes(propertyKey))
126
- .forEach(({ propertyKey }) => {
127
- const value = props[propertyKey];
128
- const patterns = this.getIndexedKey("*", propertyKey, value);
129
- pipeline.keys(patterns);
130
- });
131
- const results = await pipeline.exec();
132
- return flatKeys(results);
141
+ .map(({ propertyKey }) => this.getIndexedKey("*", propertyKey, props[propertyKey]));
142
+ if (!patterns.length) {
143
+ return [];
144
+ }
145
+ const results = await Promise.all(patterns.map((pattern) => this.callCmd(this.db, ["keys"], pattern)));
146
+ return results.flat().filter(Boolean).sort();
133
147
  }
134
148
  async insert(payload, expiresAt) {
135
149
  const id = (payload._id = payload._id || uuid());
@@ -138,41 +152,40 @@ let RedisAdapter = class RedisAdapter extends Adapter {
138
152
  const multi = this.db.multi();
139
153
  const key = this.key(id);
140
154
  const strPayload = JSON.stringify(this.serialize(payload));
141
- this.useHash ? await multi.hset(key, { payload: strPayload }) : await multi.set(key, strPayload);
155
+ if (this.useHash) {
156
+ this.callCmd(multi, ["hSet", "hset"], key, { payload: strPayload });
157
+ }
158
+ else {
159
+ this.callCmd(multi, ["set"], key, strPayload);
160
+ }
142
161
  if (expiresIn) {
143
- multi.expire(key, expiresIn);
162
+ this.callCmd(multi, ["expire"], key, expiresIn);
144
163
  }
145
164
  this.indexes.forEach(({ propertyKey }) => {
146
165
  const value = payload[propertyKey];
147
166
  const indexedKey = this.getIndexedKey(id, propertyKey, value);
148
- multi.set(indexedKey, id);
149
- expiresIn && multi.expire(indexedKey, expiresIn);
167
+ this.callCmd(multi, ["set"], indexedKey, id);
168
+ expiresIn && this.callCmd(multi, ["expire"], indexedKey, expiresIn);
150
169
  });
151
170
  await this.hooks.asyncAlter("insert", multi, [payload, expiresIn]);
152
- await multi.exec();
171
+ await this.callCmd(multi, ["exec"]);
153
172
  const result = this.deserialize(payload, { useAlias: false });
154
173
  await this.hooks.asyncEmit("afterInsert", [result]);
155
174
  return result;
156
175
  }
157
- getAllIndex(id, pipeline) {
158
- const key = ["$idx", this.key(id), "*"].join(":");
159
- return (pipeline || this.db).keys(key);
176
+ getAllIndex(id) {
177
+ const key = this.prefix(["$idx", `${this.collectionName}:${id}`, "*"].join(":"));
178
+ return this.callCmd(this.db, ["keys"], key);
160
179
  }
161
180
  getIndexedKey(id, propertyKey, value) {
162
- const key = this.key(id);
163
- return ["$idx", key, `${propertyKey}(${value})`].map(String).join(":");
181
+ const key = `${this.collectionName}:${id}`;
182
+ return this.prefix(["$idx", key, `${propertyKey}(${value})`].map(String).join(":"));
164
183
  }
165
184
  async getAll() {
166
- const keys = await this.db.keys(`${this.collectionName}:*`);
167
- const pipeline = this.db.pipeline();
168
- keys.forEach((key) => {
169
- pipeline.get(key);
170
- });
171
- const result = await pipeline.exec();
185
+ const keys = (await this.callCmd(this.db, ["keys"], this.prefix(`${this.collectionName}:*`))).sort();
186
+ const result = await Promise.all(keys.map((key) => this.callCmd(this.db, ["get"], key)));
172
187
  return (result || [])
173
- .map(([, data]) => {
174
- return typeof data === "string" ? this.deserialize(JSON.parse(data)) : undefined;
175
- })
188
+ .map((data) => (typeof data === "string" ? this.deserialize(JSON.parse(data)) : undefined))
176
189
  .filter(Boolean);
177
190
  }
178
191
  async findAllBy(props) {
@@ -182,7 +195,10 @@ let RedisAdapter = class RedisAdapter extends Adapter {
182
195
  return [];
183
196
  }
184
197
  const map = foundKeys.reduce((map, key) => {
185
- const id = getId(key);
198
+ const id = this.extractIdFromIndexedKey(key);
199
+ if (!id) {
200
+ return map;
201
+ }
186
202
  const value = map.get(id) || 0;
187
203
  return map.set(id, value + 1);
188
204
  }, new Map());
@@ -193,8 +209,6 @@ let RedisAdapter = class RedisAdapter extends Adapter {
193
209
  };
194
210
  RedisAdapter = __decorate([
195
211
  __param(0, Opts),
196
- __param(1, Inject(IOREDIS_CONNECTIONS)),
197
- __param(2, Configuration()),
198
- __metadata("design:paramtypes", [Object, Array, Object])
212
+ __metadata("design:paramtypes", [Object])
199
213
  ], RedisAdapter);
200
214
  export { RedisAdapter };
@@ -1,16 +1,17 @@
1
1
  import { AdapterModel } from "@tsed/adapters";
2
- import { Configuration } from "@tsed/di";
3
- import { IORedis } from "@tsed/ioredis";
4
- import { ChainableCommander } from "ioredis";
5
2
  import { RedisAdapter, RedisAdapterConstructorOptions } from "./RedisAdapter.js";
3
+ type ChainableCommander = any;
6
4
  export declare class OIDCRedisAdapter<T extends AdapterModel> extends RedisAdapter<T> {
7
- protected configuration: Configuration;
8
5
  protected isGrantable: boolean;
9
- constructor(options: RedisAdapterConstructorOptions, connections: IORedis[], configuration: Configuration);
10
- onInsert(multi: ChainableCommander, payload: T, expiresIn: number): Promise<ChainableCommander>;
11
- findByUid(uid: string): Promise<"" | T | null | undefined>;
12
- findByUserCode(userCode: string): Promise<"" | T | null | undefined>;
6
+ protected grantKeyFor(id: string): string;
7
+ protected userCodeKeyFor(userCode: string): string;
8
+ protected uidKeyFor(uid: string): string;
9
+ constructor(options: RedisAdapterConstructorOptions);
10
+ onInsert(multi: ChainableCommander, payload: T, expiresIn: number): Promise<any>;
11
+ findByUid(uid: string): Promise<any>;
12
+ findByUserCode(userCode: string): Promise<any>;
13
13
  destroy(id: string): Promise<void>;
14
14
  revokeByGrantId(grantId: string): Promise<void>;
15
15
  consume(id: string): Promise<void>;
16
16
  }
17
+ export {};
@@ -1,19 +1,19 @@
1
1
  import { Adapter, AdapterConstructorOptions, AdapterModel } from "@tsed/adapters";
2
- import { Configuration } from "@tsed/di";
3
2
  import { Hooks } from "@tsed/hooks";
4
- import { IORedis } from "@tsed/ioredis";
5
- import type { ChainableCommander, Redis } from "ioredis";
3
+ import { RedisConnection } from "@tsed/redis";
4
+ type Redis = any;
6
5
  export interface RedisAdapterConstructorOptions extends AdapterConstructorOptions {
7
6
  connectionName?: string;
7
+ keyPrefix?: string;
8
8
  useHash?: boolean;
9
9
  }
10
10
  export declare class RedisAdapter<Model extends AdapterModel> extends Adapter<Model> {
11
- protected configuration: Configuration;
12
11
  readonly hooks: Hooks;
13
12
  readonly connectionName: string;
14
- readonly connection: IORedis;
13
+ readonly connection: RedisConnection;
14
+ readonly keyPrefix: string;
15
15
  protected useHash: boolean;
16
- constructor(options: RedisAdapterConstructorOptions, connections: IORedis[], configuration: Configuration);
16
+ constructor(options: RedisAdapterConstructorOptions);
17
17
  get db(): Redis;
18
18
  key(id: string): string;
19
19
  create(payload: Partial<Model>, expiresAt?: Date): Promise<Model>;
@@ -26,11 +26,14 @@ export declare class RedisAdapter<Model extends AdapterModel> extends Adapter<Mo
26
26
  deleteOne(predicate: Partial<Model & any>): Promise<Model | undefined>;
27
27
  deleteById(_id: string): Promise<Model | undefined>;
28
28
  deleteMany(predicate: Partial<Model>): Promise<Model[]>;
29
+ prefix(key: string): string;
30
+ protected callCmd(target: any, methods: string[], ...args: any[]): any;
31
+ protected extractIdFromIndexedKey(key: string): string | undefined;
29
32
  protected findKeys(props: any): Promise<string[]>;
30
33
  protected insert(payload: Partial<Model>, expiresAt?: Date): Promise<any>;
31
34
  protected getAllIndex(id: string): Promise<string[]>;
32
- protected getAllIndex(id: string, pipeline: ChainableCommander): ChainableCommander;
33
35
  protected getIndexedKey(id: string, propertyKey: string, value: any): string;
34
36
  protected getAll(): Promise<Model[]>;
35
37
  protected findAllBy(props: Partial<Model & any>): Promise<Model[]>;
36
38
  }
39
+ export {};
@@ -1,9 +1,9 @@
1
1
  import { AdaptersSettings } from "@tsed/adapters";
2
- import { RedisOptions } from "ioredis";
2
+ import { RedisClientOptions } from "redis";
3
3
  declare global {
4
4
  namespace TsED {
5
5
  interface AdaptersOptions extends AdaptersSettings {
6
- redis: RedisOptions & {
6
+ redis: RedisClientOptions & {
7
7
  indexes?: {
8
8
  [propertyKey: string]: Record<string, any>;
9
9
  };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@tsed/adapters-redis",
3
3
  "description": "Redis adapter for Ts.ED",
4
4
  "type": "module",
5
- "version": "8.28.8",
5
+ "version": "8.29.0",
6
6
  "source": "./src/index.ts",
7
7
  "main": "./lib/esm/index.js",
8
8
  "module": "./lib/esm/index.js",
@@ -23,16 +23,19 @@
23
23
  "test:ci": "vitest run --coverage.thresholds.autoUpdate=true"
24
24
  },
25
25
  "dependencies": {
26
- "@tsed/adapters": "8.28.8",
27
- "@tsed/ioredis": "8.28.8",
26
+ "@tsed/adapters": "8.29.0",
27
+ "@tsed/redis": "8.29.0",
28
28
  "tslib": ">=2.7.0"
29
29
  },
30
30
  "devDependencies": {
31
- "@tsed/barrels": "8.28.8",
32
- "@tsed/core": "8.28.8",
33
- "@tsed/hooks": "8.28.8",
34
- "@tsed/typescript": "8.28.8",
31
+ "@testcontainers/redis": "^11.7.2",
32
+ "@tsed/barrels": "8.29.0",
33
+ "@tsed/core": "8.29.0",
34
+ "@tsed/di": "8.29.0",
35
+ "@tsed/hooks": "8.29.0",
36
+ "@tsed/typescript": "8.29.0",
35
37
  "eslint": "9.12.0",
38
+ "testcontainers": "11.7.2",
36
39
  "typescript": "6.0.2",
37
40
  "vitest": "4.1.4"
38
41
  },
@@ -42,8 +45,7 @@
42
45
  "@tsed/di": ">=8.0.0",
43
46
  "@tsed/hooks": ">=8.0.0",
44
47
  "@tsed/platform-http": ">=8.0.0",
45
- "ioredis": ">=5.2.3",
46
- "ioredis-mock": ">=8.2.2",
48
+ "redis": ">=5.1.0",
47
49
  "uuid": "^14.0.0"
48
50
  },
49
51
  "repository": "https://github.com/tsedio/tsed",
@@ -0,0 +1,69 @@
1
+ import {RedisContainer, StartedRedisContainer} from "@testcontainers/redis";
2
+
3
+ const KEY = "TEST_CONTAINER_REDIS";
4
+
5
+ function getGlobal<T>(key: string): T | null {
6
+ // @ts-ignore
7
+ return global[key];
8
+ }
9
+
10
+ function setGlobal(key: string, environment: unknown) {
11
+ // @ts-ignore
12
+ global[key] = environment;
13
+ }
14
+
15
+ export class TestContainersRedis {
16
+ static createContainer(image = "redis:8.0.1") {
17
+ return new RedisContainer(image).start();
18
+ }
19
+
20
+ static async startContainer(image?: string) {
21
+ // await stopMockServer()
22
+ const container = getGlobal<StartedRedisContainer>(KEY) || (await this.createContainer(image));
23
+
24
+ setGlobal(KEY, container);
25
+
26
+ process.env.REDIS_URL = container.getConnectionUrl();
27
+
28
+ return {
29
+ container,
30
+ url: process.env.REDIS_URL
31
+ };
32
+ }
33
+
34
+ static async stopContainer() {
35
+ const container = getGlobal<StartedRedisContainer>(KEY);
36
+
37
+ process.stdout.write("Stopping redis database\n");
38
+
39
+ if (container) {
40
+ await this.reset();
41
+ await container.stop();
42
+ }
43
+ }
44
+
45
+ static async reset() {
46
+ const container = getGlobal<StartedRedisContainer>(KEY);
47
+ await container?.exec("redis-cli flushall");
48
+ }
49
+
50
+ static getUrl(): string {
51
+ return process.env.REDIS_URL as string;
52
+ }
53
+
54
+ static getRedisOptions() {
55
+ const url = new URL(this.getUrl());
56
+ return {
57
+ host: url.hostname,
58
+ password: url.password,
59
+ port: url.port ? Number(url.port) : 6379,
60
+ username: url.username,
61
+ tls:
62
+ url.protocol === "rediss:"
63
+ ? {
64
+ rejectUnauthorized: false
65
+ }
66
+ : undefined
67
+ };
68
+ }
69
+ }
@@ -0,0 +1,9 @@
1
+ import {TestContainersRedis} from "./TestContainersRedis.js";
2
+
3
+ export async function setup() {
4
+ await TestContainersRedis.startContainer();
5
+ }
6
+
7
+ export async function teardown() {
8
+ await TestContainersRedis.stopContainer();
9
+ }