@discordjs/brokers 2.0.0-dev.1759017751-1c5674d9b → 2.0.0-dev.1759579293-cf89260c9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,7 +43,8 @@ These examples use [ES modules](https://nodejs.org/api/esm.html#enabling).
43
43
  import { PubSubRedisBroker } from '@discordjs/brokers';
44
44
  import Redis from 'ioredis';
45
45
 
46
- const broker = new PubSubRedisBroker(new Redis());
46
+ // Considering this only pushes events, the group and name are not important.
47
+ const broker = new PubSubRedisBroker(new Redis(), { group: 'noop', name: 'noop' });
47
48
 
48
49
  await broker.publish('test', 'Hello World!');
49
50
  await broker.destroy();
@@ -52,13 +53,22 @@ await broker.destroy();
52
53
  import { PubSubRedisBroker } from '@discordjs/brokers';
53
54
  import Redis from 'ioredis';
54
55
 
55
- const broker = new PubSubRedisBroker(new Redis());
56
+ const broker = new PubSubRedisBroker(new Redis(), {
57
+ // This is the consumer group name. You should make sure to not re-use this
58
+ // across different applications in your stack, unless you absolutely know
59
+ // what you're doing.
60
+ group: 'subscribers',
61
+ // With the assumption that this service will scale to more than one instance,
62
+ // you MUST ensure `UNIQUE_CONSUMER_ID` is unique across all of them and
63
+ // also deterministic (i.e. if instance-1 restarts, it should still be instance-1)
64
+ name: `consumer-${UNIQUE_CONSUMER_ID}`,
65
+ });
56
66
  broker.on('test', ({ data, ack }) => {
57
67
  console.log(data);
58
68
  void ack();
59
69
  });
60
70
 
61
- await broker.subscribe('subscribers', ['test']);
71
+ await broker.subscribe(['test']);
62
72
  ```
63
73
 
64
74
  ### RPC
@@ -68,7 +78,7 @@ await broker.subscribe('subscribers', ['test']);
68
78
  import { RPCRedisBroker } from '@discordjs/brokers';
69
79
  import Redis from 'ioredis';
70
80
 
71
- const broker = new RPCRedisBroker(new Redis());
81
+ const broker = new RPCRedisBroker(new Redis(), { group: 'noop', name: 'noop' });
72
82
 
73
83
  console.log(await broker.call('testcall', 'Hello World!'));
74
84
  await broker.destroy();
@@ -77,14 +87,18 @@ await broker.destroy();
77
87
  import { RPCRedisBroker } from '@discordjs/brokers';
78
88
  import Redis from 'ioredis';
79
89
 
80
- const broker = new RPCRedisBroker(new Redis());
90
+ const broker = new RPCRedisBroker(new Redis(), {
91
+ // Equivalent to the group/name in pubsub, refer to the previous example.
92
+ group: 'responders',
93
+ name: `consumer-${UNIQUE_ID}`,
94
+ });
81
95
  broker.on('testcall', ({ data, ack, reply }) => {
82
96
  console.log('responder', data);
83
97
  void ack();
84
98
  void reply(`Echo: ${data}`);
85
99
  });
86
100
 
87
- await broker.subscribe('responders', ['testcall']);
101
+ await broker.subscribe(['testcall']);
88
102
  ```
89
103
 
90
104
  ## Links
package/dist/index.d.mts CHANGED
@@ -57,9 +57,11 @@ interface IRPCBroker<TEvents extends Record<string, any[]>, TResponses extends R
57
57
  call<Event extends keyof TEvents>(event: Event, data: TEvents[Event], timeoutDuration?: number): Promise<TResponses[Event]>;
58
58
  }
59
59
 
60
+ type RedisReadGroupData = [Buffer, [Buffer, Buffer[]][]][];
60
61
  declare module 'ioredis' {
61
62
  interface Redis {
62
- xreadgroupBuffer(...args: (Buffer | string)[]): Promise<[Buffer, [Buffer, Buffer[]][]][] | null>;
63
+ xclaimBuffer(key: Buffer | string, group: Buffer | string, consumer: Buffer | string, minIdleTime: number, id: Buffer | string, ...args: (Buffer | string)[]): Promise<string[]>;
64
+ xreadgroupBuffer(...args: (Buffer | string)[]): Promise<RedisReadGroupData | null>;
63
65
  }
64
66
  }
65
67
  /**
@@ -80,19 +82,33 @@ interface RedisBrokerOptions extends BaseBrokerOptions {
80
82
  * Max number of messages to poll at once
81
83
  */
82
84
  maxChunk?: number;
85
+ /**
86
+ * How many times a message can be delivered to a consumer before it is considered dead.
87
+ * This is used to prevent messages from being stuck in the queue forever if a consumer is
88
+ * unable to process them.
89
+ */
90
+ maxDeliveredTimes?: number;
91
+ /**
92
+ * How long a message should be idle for before allowing it to be claimed by another consumer.
93
+ * Note that too high of a value can lead to a high delay in processing messages during a service downscale,
94
+ * while too low of a value can lead to messages being too eagerly claimed by other consumers during an instance
95
+ * restart (which is most likely not actually that problematic)
96
+ */
97
+ messageIdleTime?: number;
83
98
  /**
84
99
  * Unique consumer name.
85
100
  *
86
101
  * @see {@link https://redis.io/commands/xreadgroup/}
87
102
  */
88
- name?: string;
103
+ name: string;
89
104
  }
90
105
  /**
91
106
  * Default broker options for redis
92
107
  */
93
108
  declare const DefaultRedisBrokerOptions: {
94
- readonly name: string;
95
109
  readonly maxChunk: 10;
110
+ readonly maxDeliveredTimes: 3;
111
+ readonly messageIdleTime: 3000;
96
112
  readonly blockTimeout: 5000;
97
113
  readonly encode: (data: unknown) => Buffer;
98
114
  readonly decode: (data: Buffer<ArrayBufferLike>) => unknown;
@@ -132,9 +148,12 @@ declare abstract class BaseRedisBroker<TEvents extends Record<string, any[]>, TR
132
148
  */
133
149
  unsubscribe(events: (keyof TEvents)[]): Promise<void>;
134
150
  /**
135
- * Begins polling for events, firing them to {@link BaseRedisBroker.listen}
151
+ * Begins polling for events, firing them to {@link BaseRedisBroker.emitEvent}
136
152
  */
137
153
  protected listen(): Promise<void>;
154
+ private readGroup;
155
+ private processMessages;
156
+ private claimAndEmitDeadEvents;
138
157
  /**
139
158
  * Destroys the broker, closing all connections
140
159
  */
@@ -196,8 +215,9 @@ interface RPCRedisBrokerOptions extends RedisBrokerOptions {
196
215
  */
197
216
  declare const DefaultRPCRedisBrokerOptions: {
198
217
  readonly timeout: 5000;
199
- readonly name: string;
200
218
  readonly maxChunk: 10;
219
+ readonly maxDeliveredTimes: 3;
220
+ readonly messageIdleTime: 3000;
201
221
  readonly blockTimeout: 5000;
202
222
  readonly encode: (data: unknown) => Buffer;
203
223
  readonly decode: (data: Buffer<ArrayBufferLike>) => unknown;
package/dist/index.d.ts CHANGED
@@ -57,9 +57,11 @@ interface IRPCBroker<TEvents extends Record<string, any[]>, TResponses extends R
57
57
  call<Event extends keyof TEvents>(event: Event, data: TEvents[Event], timeoutDuration?: number): Promise<TResponses[Event]>;
58
58
  }
59
59
 
60
+ type RedisReadGroupData = [Buffer, [Buffer, Buffer[]][]][];
60
61
  declare module 'ioredis' {
61
62
  interface Redis {
62
- xreadgroupBuffer(...args: (Buffer | string)[]): Promise<[Buffer, [Buffer, Buffer[]][]][] | null>;
63
+ xclaimBuffer(key: Buffer | string, group: Buffer | string, consumer: Buffer | string, minIdleTime: number, id: Buffer | string, ...args: (Buffer | string)[]): Promise<string[]>;
64
+ xreadgroupBuffer(...args: (Buffer | string)[]): Promise<RedisReadGroupData | null>;
63
65
  }
64
66
  }
65
67
  /**
@@ -80,19 +82,33 @@ interface RedisBrokerOptions extends BaseBrokerOptions {
80
82
  * Max number of messages to poll at once
81
83
  */
82
84
  maxChunk?: number;
85
+ /**
86
+ * How many times a message can be delivered to a consumer before it is considered dead.
87
+ * This is used to prevent messages from being stuck in the queue forever if a consumer is
88
+ * unable to process them.
89
+ */
90
+ maxDeliveredTimes?: number;
91
+ /**
92
+ * How long a message should be idle for before allowing it to be claimed by another consumer.
93
+ * Note that too high of a value can lead to a high delay in processing messages during a service downscale,
94
+ * while too low of a value can lead to messages being too eagerly claimed by other consumers during an instance
95
+ * restart (which is most likely not actually that problematic)
96
+ */
97
+ messageIdleTime?: number;
83
98
  /**
84
99
  * Unique consumer name.
85
100
  *
86
101
  * @see {@link https://redis.io/commands/xreadgroup/}
87
102
  */
88
- name?: string;
103
+ name: string;
89
104
  }
90
105
  /**
91
106
  * Default broker options for redis
92
107
  */
93
108
  declare const DefaultRedisBrokerOptions: {
94
- readonly name: string;
95
109
  readonly maxChunk: 10;
110
+ readonly maxDeliveredTimes: 3;
111
+ readonly messageIdleTime: 3000;
96
112
  readonly blockTimeout: 5000;
97
113
  readonly encode: (data: unknown) => Buffer;
98
114
  readonly decode: (data: Buffer<ArrayBufferLike>) => unknown;
@@ -132,9 +148,12 @@ declare abstract class BaseRedisBroker<TEvents extends Record<string, any[]>, TR
132
148
  */
133
149
  unsubscribe(events: (keyof TEvents)[]): Promise<void>;
134
150
  /**
135
- * Begins polling for events, firing them to {@link BaseRedisBroker.listen}
151
+ * Begins polling for events, firing them to {@link BaseRedisBroker.emitEvent}
136
152
  */
137
153
  protected listen(): Promise<void>;
154
+ private readGroup;
155
+ private processMessages;
156
+ private claimAndEmitDeadEvents;
138
157
  /**
139
158
  * Destroys the broker, closing all connections
140
159
  */
@@ -196,8 +215,9 @@ interface RPCRedisBrokerOptions extends RedisBrokerOptions {
196
215
  */
197
216
  declare const DefaultRPCRedisBrokerOptions: {
198
217
  readonly timeout: 5000;
199
- readonly name: string;
200
218
  readonly maxChunk: 10;
219
+ readonly maxDeliveredTimes: 3;
220
+ readonly messageIdleTime: 3000;
201
221
  readonly blockTimeout: 5000;
202
222
  readonly encode: (data: unknown) => Buffer;
203
223
  readonly decode: (data: Buffer<ArrayBufferLike>) => unknown;
package/dist/index.js CHANGED
@@ -32,7 +32,6 @@ __export(index_exports, {
32
32
  module.exports = __toCommonJS(index_exports);
33
33
 
34
34
  // src/brokers/redis/BaseRedis.ts
35
- var import_node_crypto = require("crypto");
36
35
  var import_node_fs = require("fs");
37
36
  var import_node_path = require("path");
38
37
  var import_async_event_emitter = require("@vladfrangu/async_event_emitter");
@@ -52,8 +51,9 @@ var DefaultBrokerOptions = {
52
51
  // src/brokers/redis/BaseRedis.ts
53
52
  var DefaultRedisBrokerOptions = {
54
53
  ...DefaultBrokerOptions,
55
- name: (0, import_node_crypto.randomBytes)(20).toString("hex"),
56
54
  maxChunk: 10,
55
+ maxDeliveredTimes: 3,
56
+ messageIdleTime: 3e3,
57
57
  blockTimeout: 5e3
58
58
  };
59
59
  var BaseRedisBroker = class extends import_async_event_emitter.AsyncEventEmitter {
@@ -125,7 +125,7 @@ var BaseRedisBroker = class extends import_async_event_emitter.AsyncEventEmitter
125
125
  }
126
126
  }
127
127
  /**
128
- * Begins polling for events, firing them to {@link BaseRedisBroker.listen}
128
+ * Begins polling for events, firing them to {@link BaseRedisBroker.emitEvent}
129
129
  */
130
130
  async listen() {
131
131
  if (this.listening) {
@@ -134,34 +134,16 @@ var BaseRedisBroker = class extends import_async_event_emitter.AsyncEventEmitter
134
134
  this.listening = true;
135
135
  while (this.subscribedEvents.size > 0) {
136
136
  try {
137
- const data = await this.streamReadClient.xreadgroupBuffer(
138
- "GROUP",
139
- this.options.group,
140
- this.options.name,
141
- "COUNT",
142
- String(this.options.maxChunk),
143
- "BLOCK",
144
- String(this.options.blockTimeout),
145
- "STREAMS",
146
- ...this.subscribedEvents,
147
- ...Array.from({ length: this.subscribedEvents.size }, () => ">")
148
- );
137
+ await this.claimAndEmitDeadEvents();
138
+ } catch (error) {
139
+ this.emit("error", error);
140
+ }
141
+ try {
142
+ const data = await this.readGroup(">", this.options.blockTimeout);
149
143
  if (!data) {
150
144
  continue;
151
145
  }
152
- for (const [event, info] of data) {
153
- for (const [id, packet] of info) {
154
- const idx = packet.findIndex((value, idx2) => value.toString("utf8") === "data" && idx2 % 2 === 0);
155
- if (idx < 0) {
156
- continue;
157
- }
158
- const data2 = packet[idx + 1];
159
- if (!data2) {
160
- continue;
161
- }
162
- this.emitEvent(id, this.options.group, event.toString("utf8"), this.options.decode(data2));
163
- }
164
- }
146
+ await this.processMessages(data);
165
147
  } catch (error) {
166
148
  this.emit("error", error);
167
149
  break;
@@ -169,6 +151,81 @@ var BaseRedisBroker = class extends import_async_event_emitter.AsyncEventEmitter
169
151
  }
170
152
  this.listening = false;
171
153
  }
154
+ async readGroup(fromId, block) {
155
+ const data = await this.streamReadClient.xreadgroupBuffer(
156
+ "GROUP",
157
+ this.options.group,
158
+ this.options.name,
159
+ "COUNT",
160
+ String(this.options.maxChunk),
161
+ "BLOCK",
162
+ String(block),
163
+ "STREAMS",
164
+ ...this.subscribedEvents,
165
+ ...Array.from({ length: this.subscribedEvents.size }, () => fromId)
166
+ );
167
+ return data ?? [];
168
+ }
169
+ async processMessages(data) {
170
+ for (const [event, messages] of data) {
171
+ const eventName = event.toString("utf8");
172
+ for (const [id, packet] of messages) {
173
+ const idx = packet.findIndex((value, idx2) => value.toString("utf8") === "data" && idx2 % 2 === 0);
174
+ if (idx < 0) continue;
175
+ const payload = packet[idx + 1];
176
+ if (!payload) continue;
177
+ this.emitEvent(id, this.options.group, eventName, this.options.decode(payload));
178
+ }
179
+ }
180
+ }
181
+ async claimAndEmitDeadEvents() {
182
+ for (const stream of this.subscribedEvents) {
183
+ const pending = await this.streamReadClient.xpending(
184
+ stream,
185
+ this.options.group,
186
+ "-",
187
+ "+",
188
+ this.options.maxChunk
189
+ // See: https://redis.io/docs/latest/commands/xpending/#extended-form-of-xpending
190
+ );
191
+ for (const [id, consumer, idleMs, deliveredTimes] of pending) {
192
+ if (idleMs < this.options.messageIdleTime) {
193
+ continue;
194
+ }
195
+ if (deliveredTimes > this.options.maxDeliveredTimes) {
196
+ await this.streamReadClient.xdel(stream, this.options.group, id);
197
+ continue;
198
+ }
199
+ if (consumer !== this.options.name) {
200
+ const claimed = await this.streamReadClient.xclaimBuffer(
201
+ stream,
202
+ this.options.group,
203
+ this.options.name,
204
+ Math.max(this.options.messageIdleTime, 1),
205
+ id,
206
+ "JUSTID"
207
+ );
208
+ if (!claimed?.length) {
209
+ continue;
210
+ }
211
+ }
212
+ const entries = await this.streamReadClient.xrangeBuffer(stream, id, id);
213
+ if (!entries?.length) {
214
+ continue;
215
+ }
216
+ const [msgId, fields] = entries[0];
217
+ const idx = fields.findIndex((value, idx2) => value.toString("utf8") === "data" && idx2 % 2 === 0);
218
+ if (idx < 0) {
219
+ continue;
220
+ }
221
+ const payload = fields[idx + 1];
222
+ if (!payload) {
223
+ continue;
224
+ }
225
+ this.emitEvent(msgId, this.options.group, stream, this.options.decode(payload));
226
+ }
227
+ }
228
+ }
172
229
  /**
173
230
  * Destroys the broker, closing all connections
174
231
  */
@@ -267,7 +324,7 @@ var RPCRedisBroker = class extends BaseRedisBroker {
267
324
  };
268
325
 
269
326
  // src/index.ts
270
- var version = "2.0.0-dev.1759017751-1c5674d9b";
327
+ var version = "2.0.0-dev.1759579293-cf89260c9";
271
328
  // Annotate the CommonJS export names for ESM import in node:
272
329
  0 && (module.exports = {
273
330
  BaseRedisBroker,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/brokers/redis/BaseRedis.ts","../src/brokers/Broker.ts","../src/brokers/redis/PubSubRedis.ts","../src/brokers/redis/RPCRedis.ts"],"sourcesContent":["export * from './brokers/redis/BaseRedis.js';\nexport * from './brokers/redis/PubSubRedis.js';\nexport * from './brokers/redis/RPCRedis.js';\n\nexport * from './brokers/Broker.js';\n\n/**\n * The {@link https://github.com/discordjs/discord.js/blob/main/packages/brokers#readme | @discordjs/brokers} version\n * that you are currently using.\n *\n * @privateRemarks This needs to explicitly be `string` so it is not typed as a \"const string\" that gets injected by esbuild.\n */\nexport const version = '2.0.0-dev.1759017751-1c5674d9b' as string;\n","import type { Buffer } from 'node:buffer';\nimport { randomBytes } from 'node:crypto';\nimport { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';\nimport type { Redis } from 'ioredis';\nimport { ReplyError } from 'ioredis';\nimport type { BaseBrokerOptions, IBaseBroker, ToEventMap } from '../Broker.js';\nimport { DefaultBrokerOptions } from '../Broker.js';\n\n// For some reason ioredis doesn't have this typed, but it exists\ndeclare module 'ioredis' {\n\tinterface Redis {\n\t\txreadgroupBuffer(...args: (Buffer | string)[]): Promise<[Buffer, [Buffer, Buffer[]][]][] | null>;\n\t}\n}\n\n/**\n * Options specific for a Redis broker\n */\nexport interface RedisBrokerOptions extends BaseBrokerOptions {\n\t/**\n\t * How long to block for messages when polling\n\t */\n\tblockTimeout?: number;\n\n\t/**\n\t * Consumer group name to use for this broker\n\t *\n\t * @see {@link https://redis.io/commands/xreadgroup/}\n\t */\n\tgroup: string;\n\n\t/**\n\t * Max number of messages to poll at once\n\t */\n\tmaxChunk?: number;\n\n\t/**\n\t * Unique consumer name.\n\t *\n\t * @see {@link https://redis.io/commands/xreadgroup/}\n\t */\n\tname?: string;\n}\n\n/**\n * Default broker options for redis\n */\nexport const DefaultRedisBrokerOptions = {\n\t...DefaultBrokerOptions,\n\tname: randomBytes(20).toString('hex'),\n\tmaxChunk: 10,\n\tblockTimeout: 5_000,\n} as const satisfies Required<Omit<RedisBrokerOptions, 'group'>>;\n\n/**\n * Helper class with shared Redis logic\n */\nexport abstract class BaseRedisBroker<\n\t\tTEvents extends Record<string, any[]>,\n\t\tTResponses extends Record<keyof TEvents, any> | undefined = undefined,\n\t>\n\textends AsyncEventEmitter<ToEventMap<TEvents, TResponses>>\n\timplements IBaseBroker<TEvents>\n{\n\t/**\n\t * Used for Redis queues, see the 3rd argument taken by {@link https://redis.io/commands/xadd | xadd}\n\t */\n\tpublic static readonly STREAM_DATA_KEY = 'data' as const;\n\n\t/**\n\t * Options this broker is using\n\t */\n\tprotected readonly options: Required<RedisBrokerOptions>;\n\n\t/**\n\t * Events this broker has subscribed to\n\t */\n\tprotected readonly subscribedEvents = new Set<string>();\n\n\t/**\n\t * Internal copy of the Redis client being used to read incoming payloads\n\t */\n\tprotected readonly streamReadClient: Redis;\n\n\t/**\n\t * Whether this broker is currently polling events\n\t */\n\tprotected listening = false;\n\n\tpublic constructor(\n\t\tprotected readonly redisClient: Redis,\n\t\toptions: RedisBrokerOptions,\n\t) {\n\t\tsuper();\n\t\tthis.options = { ...DefaultRedisBrokerOptions, ...options };\n\t\tredisClient.defineCommand('xcleangroup', {\n\t\t\tnumberOfKeys: 1,\n\t\t\tlua: readFileSync(resolve(__dirname, '..', 'scripts', 'xcleangroup.lua'), 'utf8'),\n\t\t});\n\t\tthis.streamReadClient = redisClient.duplicate();\n\t}\n\n\t/**\n\t * {@inheritDoc IBaseBroker.subscribe}\n\t */\n\tpublic async subscribe(events: (keyof TEvents)[]): Promise<void> {\n\t\tawait Promise.all(\n\t\t\t// @ts-expect-error: Intended\n\t\t\tevents.map(async (event) => {\n\t\t\t\tthis.subscribedEvents.add(event as string);\n\t\t\t\ttry {\n\t\t\t\t\treturn await this.redisClient.xgroup('CREATE', event as string, this.options.group, 0, 'MKSTREAM');\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!(error instanceof ReplyError)) {\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\t\tvoid this.listen();\n\t}\n\n\t/**\n\t * {@inheritDoc IBaseBroker.unsubscribe}\n\t */\n\tpublic async unsubscribe(events: (keyof TEvents)[]): Promise<void> {\n\t\tconst commands: unknown[][] = Array.from({ length: events.length * 2 });\n\t\tfor (let idx = 0; idx < commands.length; idx += 2) {\n\t\t\tconst event = events[idx / 2];\n\t\t\tcommands[idx] = ['xgroup', 'delconsumer', event as string, this.options.group, this.options.name];\n\t\t\tcommands[idx + 1] = ['xcleangroup', event as string, this.options.group];\n\t\t}\n\n\t\tawait this.redisClient.pipeline(commands).exec();\n\n\t\tfor (const event of events) {\n\t\t\tthis.subscribedEvents.delete(event as string);\n\t\t}\n\t}\n\n\t/**\n\t * Begins polling for events, firing them to {@link BaseRedisBroker.listen}\n\t */\n\tprotected async listen(): Promise<void> {\n\t\tif (this.listening) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.listening = true;\n\n\t\twhile (this.subscribedEvents.size > 0) {\n\t\t\ttry {\n\t\t\t\tconst data = await this.streamReadClient.xreadgroupBuffer(\n\t\t\t\t\t'GROUP',\n\t\t\t\t\tthis.options.group,\n\t\t\t\t\tthis.options.name,\n\t\t\t\t\t'COUNT',\n\t\t\t\t\tString(this.options.maxChunk),\n\t\t\t\t\t'BLOCK',\n\t\t\t\t\tString(this.options.blockTimeout),\n\t\t\t\t\t'STREAMS',\n\t\t\t\t\t...this.subscribedEvents,\n\t\t\t\t\t...Array.from({ length: this.subscribedEvents.size }, () => '>'),\n\t\t\t\t);\n\n\t\t\t\tif (!data) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tfor (const [event, info] of data) {\n\t\t\t\t\tfor (const [id, packet] of info) {\n\t\t\t\t\t\tconst idx = packet.findIndex((value, idx) => value.toString('utf8') === 'data' && idx % 2 === 0);\n\t\t\t\t\t\tif (idx < 0) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst data = packet[idx + 1];\n\t\t\t\t\t\tif (!data) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.emitEvent(id, this.options.group, event.toString('utf8'), this.options.decode(data));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// @ts-expect-error: Intended\n\t\t\t\tthis.emit('error', error);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tthis.listening = false;\n\t}\n\n\t/**\n\t * Destroys the broker, closing all connections\n\t */\n\tpublic async destroy() {\n\t\tawait this.unsubscribe([...this.subscribedEvents]);\n\t\tthis.streamReadClient.disconnect();\n\t\tthis.redisClient.disconnect();\n\t}\n\n\t/**\n\t * Handles an incoming Redis event\n\t */\n\tprotected abstract emitEvent(id: Buffer, group: string, event: string, data: unknown): unknown;\n}\n","import { Buffer } from 'node:buffer';\nimport { encode, decode } from '@msgpack/msgpack';\nimport type { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';\n\n/**\n * Base options for a broker implementation\n */\nexport interface BaseBrokerOptions {\n\t/**\n\t * Function to use for decoding messages\n\t */\n\t// eslint-disable-next-line @typescript-eslint/method-signature-style\n\tdecode?: (data: Buffer) => unknown;\n\t/**\n\t * Function to use for encoding messages\n\t */\n\t// eslint-disable-next-line @typescript-eslint/method-signature-style\n\tencode?: (data: unknown) => Buffer;\n}\n\n/**\n * Default broker options\n */\nexport const DefaultBrokerOptions = {\n\tencode: (data): Buffer => {\n\t\tconst encoded = encode(data);\n\t\treturn Buffer.from(encoded.buffer, encoded.byteOffset, encoded.byteLength);\n\t},\n\tdecode: (data): unknown => decode(data),\n} as const satisfies Required<BaseBrokerOptions>;\n\nexport type ToEventMap<\n\tTRecord extends Record<string, any[]>,\n\tTResponses extends Record<keyof TRecord, any> | undefined = undefined,\n> = {\n\t[TKey in keyof TRecord]: [\n\t\tevent: TResponses extends Record<keyof TRecord, any>\n\t\t\t? { ack(): Promise<void>; reply(data: TResponses[TKey]): Promise<void> }\n\t\t\t: { ack(): Promise<void>; data: TRecord[TKey] },\n\t];\n};\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface IBaseBroker<TEvents extends {}> {\n\t/**\n\t * Subscribes to the given events\n\t */\n\tsubscribe(events: (keyof TEvents)[]): Promise<void>;\n\t/**\n\t * Unsubscribes from the given events\n\t */\n\tunsubscribe(events: (keyof TEvents)[]): Promise<void>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface IPubSubBroker<TEvents extends {}>\n\textends IBaseBroker<TEvents>,\n\t\tAsyncEventEmitter<ToEventMap<TEvents>> {\n\t/**\n\t * Publishes an event\n\t */\n\tpublish<Event extends keyof TEvents>(event: Event, data: TEvents[Event]): Promise<void>;\n}\n\nexport interface IRPCBroker<TEvents extends Record<string, any[]>, TResponses extends Record<keyof TEvents, any>>\n\textends IBaseBroker<TEvents>,\n\t\tAsyncEventEmitter<ToEventMap<TEvents, TResponses>> {\n\t/**\n\t * Makes an RPC call\n\t */\n\tcall<Event extends keyof TEvents>(\n\t\tevent: Event,\n\t\tdata: TEvents[Event],\n\t\ttimeoutDuration?: number,\n\t): Promise<TResponses[Event]>;\n}\n","import type { Buffer } from 'node:buffer';\nimport type { IPubSubBroker } from '../Broker.js';\nimport { BaseRedisBroker } from './BaseRedis.js';\n\n/**\n * PubSub broker powered by Redis\n *\n * @example\n * ```ts\n * // publisher.js\n * import { PubSubRedisBroker } from '@discordjs/brokers';\n * import Redis from 'ioredis';\n *\n * const broker = new PubSubRedisBroker(new Redis());\n *\n * await broker.publish('test', 'Hello World!');\n * await broker.destroy();\n *\n * // subscriber.js\n * import { PubSubRedisBroker } from '@discordjs/brokers';\n * import Redis from 'ioredis';\n *\n * const broker = new PubSubRedisBroker(new Redis());\n * \tbroker.on('test', ({ data, ack }) => {\n * \tconsole.log(data);\n * \tvoid ack();\n * });\n *\n * await broker.subscribe('subscribers', ['test']);\n * ```\n */\nexport class PubSubRedisBroker<TEvents extends Record<string, any>>\n\textends BaseRedisBroker<TEvents>\n\timplements IPubSubBroker<TEvents>\n{\n\t/**\n\t * {@inheritDoc IPubSubBroker.publish}\n\t */\n\tpublic async publish<Event extends keyof TEvents>(event: Event, data: TEvents[Event]): Promise<void> {\n\t\tawait this.redisClient.xadd(event as string, '*', BaseRedisBroker.STREAM_DATA_KEY, this.options.encode(data));\n\t}\n\n\tprotected emitEvent(id: Buffer, group: string, event: string, data: unknown) {\n\t\tconst payload: { ack(): Promise<void>; data: unknown } = {\n\t\t\tdata,\n\t\t\tack: async () => {\n\t\t\t\tawait this.redisClient.xack(event, group, id);\n\t\t\t},\n\t\t};\n\n\t\t// @ts-expect-error: Intended\n\t\tthis.emit(event, payload);\n\t}\n}\n","import type { Buffer } from 'node:buffer';\nimport { clearTimeout, setTimeout } from 'node:timers';\nimport type Redis from 'ioredis/built/Redis.js';\nimport type { IRPCBroker } from '../Broker.js';\nimport type { RedisBrokerOptions } from './BaseRedis.js';\nimport { BaseRedisBroker, DefaultRedisBrokerOptions } from './BaseRedis.js';\n\ninterface InternalPromise {\n\treject(error: any): void;\n\tresolve(data: any): void;\n\ttimeout: NodeJS.Timeout;\n}\n\n/**\n * Options specific for an RPC Redis broker\n */\nexport interface RPCRedisBrokerOptions extends RedisBrokerOptions {\n\ttimeout?: number;\n}\n\n/**\n * Default values used for the {@link RPCRedisBrokerOptions}\n */\nexport const DefaultRPCRedisBrokerOptions = {\n\t...DefaultRedisBrokerOptions,\n\ttimeout: 5_000,\n} as const satisfies Required<Omit<RPCRedisBrokerOptions, 'group'>>;\n\n/**\n * RPC broker powered by Redis\n *\n * @example\n * ```ts\n * // caller.js\n * import { RPCRedisBroker } from '@discordjs/brokers';\n * import Redis from 'ioredis';\n *\n * const broker = new RPCRedisBroker(new Redis());\n *\n * console.log(await broker.call('testcall', 'Hello World!'));\n * await broker.destroy();\n *\n * // responder.js\n * import { RPCRedisBroker } from '@discordjs/brokers';\n * import Redis from 'ioredis';\n *\n * const broker = new RPCRedisBroker(new Redis());\n * broker.on('testcall', ({ data, ack, reply }) => {\n * \tconsole.log('responder', data);\n * \tvoid ack();\n * \tvoid reply(`Echo: ${data}`);\n * });\n *\n * await broker.subscribe('responders', ['testcall']);\n * ```\n */\nexport class RPCRedisBroker<TEvents extends Record<string, any[]>, TResponses extends Record<keyof TEvents, any>>\n\textends BaseRedisBroker<TEvents, TResponses>\n\timplements IRPCBroker<TEvents, TResponses>\n{\n\t/**\n\t * Options this broker is using\n\t */\n\tprotected override readonly options: Required<RPCRedisBrokerOptions>;\n\n\t/**\n\t * @internal\n\t */\n\tprotected readonly promises = new Map<string, InternalPromise>();\n\n\tpublic constructor(redisClient: Redis, options: RPCRedisBrokerOptions) {\n\t\tsuper(redisClient, options);\n\t\tthis.options = { ...DefaultRPCRedisBrokerOptions, ...options };\n\n\t\tthis.streamReadClient.on('messageBuffer', (channel: Buffer, message: Buffer) => {\n\t\t\tconst [, id] = channel.toString().split(':');\n\t\t\tif (id && this.promises.has(id)) {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\t\tconst { resolve, timeout } = this.promises.get(id)!;\n\t\t\t\tresolve(this.options.decode(message));\n\t\t\t\tclearTimeout(timeout);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * {@inheritDoc IRPCBroker.call}\n\t */\n\tpublic async call<Event extends keyof TEvents>(\n\t\tevent: Event,\n\t\tdata: TEvents[Event],\n\t\ttimeoutDuration: number = this.options.timeout,\n\t): Promise<TResponses[Event]> {\n\t\tconst id = await this.redisClient.xadd(\n\t\t\tevent as string,\n\t\t\t'*',\n\t\t\tBaseRedisBroker.STREAM_DATA_KEY,\n\t\t\tthis.options.encode(data),\n\t\t);\n\t\t// This id! assertion is valid. From redis docs:\n\t\t// \"The command returns a Null reply when used with the NOMKSTREAM option and the key doesn't exist.\"\n\t\t// See: https://redis.io/commands/xadd/\n\t\tconst rpcChannel = `${event as string}:${id!}`;\n\n\t\t// Construct the error here for better stack traces\n\t\tconst timedOut = new Error(`timed out after ${timeoutDuration}ms`);\n\n\t\tawait this.streamReadClient.subscribe(rpcChannel);\n\t\treturn new Promise<TResponses[Event]>((resolve, reject) => {\n\t\t\tconst timeout = setTimeout(() => reject(timedOut), timeoutDuration).unref();\n\n\t\t\tthis.promises.set(id!, { resolve, reject, timeout });\n\t\t\t// eslint-disable-next-line promise/prefer-await-to-then\n\t\t}).finally(() => {\n\t\t\tvoid this.streamReadClient.unsubscribe(rpcChannel);\n\t\t\tthis.promises.delete(id!);\n\t\t});\n\t}\n\n\tprotected emitEvent(id: Buffer, event: string, data: unknown) {\n\t\tconst payload: { ack(): Promise<void>; data: unknown; reply(data: unknown): Promise<void> } = {\n\t\t\tdata,\n\t\t\tack: async () => {\n\t\t\t\tawait this.redisClient.xack(event, this.options.group, id);\n\t\t\t},\n\t\t\treply: async (data) => {\n\t\t\t\tawait this.redisClient.publish(`${event}:${id.toString()}`, this.options.encode(data));\n\t\t\t},\n\t\t};\n\n\t\t// @ts-expect-error: Intended\n\t\tthis.emit(event, payload);\n\t}\n}\n"],"mappings":"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","names":["idx","data","resolve","data"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/brokers/redis/BaseRedis.ts","../src/brokers/Broker.ts","../src/brokers/redis/PubSubRedis.ts","../src/brokers/redis/RPCRedis.ts"],"sourcesContent":["export * from './brokers/redis/BaseRedis.js';\nexport * from './brokers/redis/PubSubRedis.js';\nexport * from './brokers/redis/RPCRedis.js';\n\nexport * from './brokers/Broker.js';\n\n/**\n * The {@link https://github.com/discordjs/discord.js/blob/main/packages/brokers#readme | @discordjs/brokers} version\n * that you are currently using.\n *\n * @privateRemarks This needs to explicitly be `string` so it is not typed as a \"const string\" that gets injected by esbuild.\n */\nexport const version = '2.0.0-dev.1759579293-cf89260c9' as string;\n","import type { Buffer } from 'node:buffer';\nimport { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';\nimport type { Redis } from 'ioredis';\nimport { ReplyError } from 'ioredis';\nimport type { BaseBrokerOptions, IBaseBroker, ToEventMap } from '../Broker.js';\nimport { DefaultBrokerOptions } from '../Broker.js';\n\ntype RedisReadGroupData = [Buffer, [Buffer, Buffer[]][]][];\n\n// For some reason ioredis doesn't have those typed, but they exist\ndeclare module 'ioredis' {\n\tinterface Redis {\n\t\txclaimBuffer(\n\t\t\tkey: Buffer | string,\n\t\t\tgroup: Buffer | string,\n\t\t\tconsumer: Buffer | string,\n\t\t\tminIdleTime: number,\n\t\t\tid: Buffer | string,\n\t\t\t...args: (Buffer | string)[]\n\t\t): Promise<string[]>;\n\t\txreadgroupBuffer(...args: (Buffer | string)[]): Promise<RedisReadGroupData | null>;\n\t}\n}\n\n/**\n * Options specific for a Redis broker\n */\nexport interface RedisBrokerOptions extends BaseBrokerOptions {\n\t/**\n\t * How long to block for messages when polling\n\t */\n\tblockTimeout?: number;\n\t/**\n\t * Consumer group name to use for this broker\n\t *\n\t * @see {@link https://redis.io/commands/xreadgroup/}\n\t */\n\tgroup: string;\n\t/**\n\t * Max number of messages to poll at once\n\t */\n\tmaxChunk?: number;\n\t/**\n\t * How many times a message can be delivered to a consumer before it is considered dead.\n\t * This is used to prevent messages from being stuck in the queue forever if a consumer is\n\t * unable to process them.\n\t */\n\tmaxDeliveredTimes?: number;\n\t/**\n\t * How long a message should be idle for before allowing it to be claimed by another consumer.\n\t * Note that too high of a value can lead to a high delay in processing messages during a service downscale,\n\t * while too low of a value can lead to messages being too eagerly claimed by other consumers during an instance\n\t * restart (which is most likely not actually that problematic)\n\t */\n\tmessageIdleTime?: number;\n\t/**\n\t * Unique consumer name.\n\t *\n\t * @see {@link https://redis.io/commands/xreadgroup/}\n\t */\n\tname: string;\n}\n\n/**\n * Default broker options for redis\n */\nexport const DefaultRedisBrokerOptions = {\n\t...DefaultBrokerOptions,\n\tmaxChunk: 10,\n\tmaxDeliveredTimes: 3,\n\tmessageIdleTime: 3_000,\n\tblockTimeout: 5_000,\n} as const satisfies Required<Omit<RedisBrokerOptions, 'group' | 'name'>>;\n\n/**\n * Helper class with shared Redis logic\n */\nexport abstract class BaseRedisBroker<\n\t\tTEvents extends Record<string, any[]>,\n\t\tTResponses extends Record<keyof TEvents, any> | undefined = undefined,\n\t>\n\textends AsyncEventEmitter<ToEventMap<TEvents, TResponses>>\n\timplements IBaseBroker<TEvents>\n{\n\t/**\n\t * Used for Redis queues, see the 3rd argument taken by {@link https://redis.io/commands/xadd | xadd}\n\t */\n\tpublic static readonly STREAM_DATA_KEY = 'data' as const;\n\n\t/**\n\t * Options this broker is using\n\t */\n\tprotected readonly options: Required<RedisBrokerOptions>;\n\n\t/**\n\t * Events this broker has subscribed to\n\t */\n\tprotected readonly subscribedEvents = new Set<string>();\n\n\t/**\n\t * Internal copy of the Redis client being used to read incoming payloads\n\t */\n\tprotected readonly streamReadClient: Redis;\n\n\t/**\n\t * Whether this broker is currently polling events\n\t */\n\tprotected listening = false;\n\n\tpublic constructor(\n\t\tprotected readonly redisClient: Redis,\n\t\toptions: RedisBrokerOptions,\n\t) {\n\t\tsuper();\n\t\tthis.options = { ...DefaultRedisBrokerOptions, ...options };\n\t\tredisClient.defineCommand('xcleangroup', {\n\t\t\tnumberOfKeys: 1,\n\t\t\tlua: readFileSync(resolve(__dirname, '..', 'scripts', 'xcleangroup.lua'), 'utf8'),\n\t\t});\n\t\tthis.streamReadClient = redisClient.duplicate();\n\t}\n\n\t/**\n\t * {@inheritDoc IBaseBroker.subscribe}\n\t */\n\tpublic async subscribe(events: (keyof TEvents)[]): Promise<void> {\n\t\tawait Promise.all(\n\t\t\t// @ts-expect-error: Intended\n\t\t\tevents.map(async (event) => {\n\t\t\t\tthis.subscribedEvents.add(event as string);\n\t\t\t\ttry {\n\t\t\t\t\treturn await this.redisClient.xgroup('CREATE', event as string, this.options.group, 0, 'MKSTREAM');\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!(error instanceof ReplyError)) {\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\t\tvoid this.listen();\n\t}\n\n\t/**\n\t * {@inheritDoc IBaseBroker.unsubscribe}\n\t */\n\tpublic async unsubscribe(events: (keyof TEvents)[]): Promise<void> {\n\t\tconst commands: unknown[][] = Array.from({ length: events.length * 2 });\n\t\tfor (let idx = 0; idx < commands.length; idx += 2) {\n\t\t\tconst event = events[idx / 2];\n\t\t\tcommands[idx] = ['xgroup', 'delconsumer', event as string, this.options.group, this.options.name];\n\t\t\tcommands[idx + 1] = ['xcleangroup', event as string, this.options.group];\n\t\t}\n\n\t\tawait this.redisClient.pipeline(commands).exec();\n\n\t\tfor (const event of events) {\n\t\t\tthis.subscribedEvents.delete(event as string);\n\t\t}\n\t}\n\n\t/**\n\t * Begins polling for events, firing them to {@link BaseRedisBroker.emitEvent}\n\t */\n\tprotected async listen(): Promise<void> {\n\t\tif (this.listening) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.listening = true;\n\n\t\t// Enter regular polling\n\t\twhile (this.subscribedEvents.size > 0) {\n\t\t\ttry {\n\t\t\t\tawait this.claimAndEmitDeadEvents();\n\t\t\t} catch (error) {\n\t\t\t\t// @ts-expect-error: Intended\n\t\t\t\tthis.emit('error', error);\n\t\t\t\t// We don't break here to keep the loop running even if dead event processing fails\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// As per docs, '>' means \"give me a new message\"\n\t\t\t\tconst data = await this.readGroup('>', this.options.blockTimeout);\n\t\t\t\tif (!data) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tawait this.processMessages(data);\n\t\t\t} catch (error) {\n\t\t\t\t// @ts-expect-error: Intended\n\t\t\t\tthis.emit('error', error);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tthis.listening = false;\n\t}\n\n\tprivate async readGroup(fromId: string, block: number): Promise<RedisReadGroupData> {\n\t\tconst data = await this.streamReadClient.xreadgroupBuffer(\n\t\t\t'GROUP',\n\t\t\tthis.options.group,\n\t\t\tthis.options.name,\n\t\t\t'COUNT',\n\t\t\tString(this.options.maxChunk),\n\t\t\t'BLOCK',\n\t\t\tString(block),\n\t\t\t'STREAMS',\n\t\t\t...this.subscribedEvents,\n\t\t\t...Array.from({ length: this.subscribedEvents.size }, () => fromId),\n\t\t);\n\n\t\treturn data ?? [];\n\t}\n\n\tprivate async processMessages(data: RedisReadGroupData): Promise<void> {\n\t\tfor (const [event, messages] of data) {\n\t\t\tconst eventName = event.toString('utf8');\n\n\t\t\tfor (const [id, packet] of messages) {\n\t\t\t\tconst idx = packet.findIndex((value, idx) => value.toString('utf8') === 'data' && idx % 2 === 0);\n\t\t\t\tif (idx < 0) continue;\n\n\t\t\t\tconst payload = packet[idx + 1];\n\t\t\t\tif (!payload) continue;\n\n\t\t\t\tthis.emitEvent(id, this.options.group, eventName, this.options.decode(payload));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async claimAndEmitDeadEvents(): Promise<void> {\n\t\tfor (const stream of this.subscribedEvents) {\n\t\t\t// Get up to N oldest pending messages (note: a pending message is a message that has been read, but never ACKed)\n\t\t\tconst pending = (await this.streamReadClient.xpending(\n\t\t\t\tstream,\n\t\t\t\tthis.options.group,\n\t\t\t\t'-',\n\t\t\t\t'+',\n\t\t\t\tthis.options.maxChunk,\n\t\t\t\t// See: https://redis.io/docs/latest/commands/xpending/#extended-form-of-xpending\n\t\t\t)) as [id: string, consumer: string, idleMs: number, deliveredTimes: number][];\n\n\t\t\tfor (const [id, consumer, idleMs, deliveredTimes] of pending) {\n\t\t\t\t// Technically xclaim checks for us anyway, but why not avoid an extra call?\n\t\t\t\tif (idleMs < this.options.messageIdleTime) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (deliveredTimes > this.options.maxDeliveredTimes) {\n\t\t\t\t\t// This message is dead. It has repeatedly failed being processed by a consumer.\n\t\t\t\t\tawait this.streamReadClient.xdel(stream, this.options.group, id);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Try to claim the message if we don't already own it (this may fail if another consumer has already claimed it)\n\t\t\t\tif (consumer !== this.options.name) {\n\t\t\t\t\tconst claimed = await this.streamReadClient.xclaimBuffer(\n\t\t\t\t\t\tstream,\n\t\t\t\t\t\tthis.options.group,\n\t\t\t\t\t\tthis.options.name,\n\t\t\t\t\t\tMath.max(this.options.messageIdleTime, 1),\n\t\t\t\t\t\tid,\n\t\t\t\t\t\t'JUSTID',\n\t\t\t\t\t);\n\n\t\t\t\t\t// Another consumer got the message before us\n\t\t\t\t\tif (!claimed?.length) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Fetch message body\n\t\t\t\tconst entries = await this.streamReadClient.xrangeBuffer(stream, id, id);\n\t\t\t\t// No idea how this could happen, frankly!\n\t\t\t\tif (!entries?.length) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst [msgId, fields] = entries[0]!;\n\t\t\t\tconst idx = fields.findIndex((value, idx) => value.toString('utf8') === 'data' && idx % 2 === 0);\n\t\t\t\tif (idx < 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst payload = fields[idx + 1];\n\t\t\t\tif (!payload) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tthis.emitEvent(msgId, this.options.group, stream, this.options.decode(payload));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Destroys the broker, closing all connections\n\t */\n\tpublic async destroy() {\n\t\tawait this.unsubscribe([...this.subscribedEvents]);\n\t\tthis.streamReadClient.disconnect();\n\t\tthis.redisClient.disconnect();\n\t}\n\n\t/**\n\t * Handles an incoming Redis event\n\t */\n\tprotected abstract emitEvent(id: Buffer, group: string, event: string, data: unknown): unknown;\n}\n","import { Buffer } from 'node:buffer';\nimport { encode, decode } from '@msgpack/msgpack';\nimport type { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';\n\n/**\n * Base options for a broker implementation\n */\nexport interface BaseBrokerOptions {\n\t/**\n\t * Function to use for decoding messages\n\t */\n\t// eslint-disable-next-line @typescript-eslint/method-signature-style\n\tdecode?: (data: Buffer) => unknown;\n\t/**\n\t * Function to use for encoding messages\n\t */\n\t// eslint-disable-next-line @typescript-eslint/method-signature-style\n\tencode?: (data: unknown) => Buffer;\n}\n\n/**\n * Default broker options\n */\nexport const DefaultBrokerOptions = {\n\tencode: (data): Buffer => {\n\t\tconst encoded = encode(data);\n\t\treturn Buffer.from(encoded.buffer, encoded.byteOffset, encoded.byteLength);\n\t},\n\tdecode: (data): unknown => decode(data),\n} as const satisfies Required<BaseBrokerOptions>;\n\nexport type ToEventMap<\n\tTRecord extends Record<string, any[]>,\n\tTResponses extends Record<keyof TRecord, any> | undefined = undefined,\n> = {\n\t[TKey in keyof TRecord]: [\n\t\tevent: TResponses extends Record<keyof TRecord, any>\n\t\t\t? { ack(): Promise<void>; reply(data: TResponses[TKey]): Promise<void> }\n\t\t\t: { ack(): Promise<void>; data: TRecord[TKey] },\n\t];\n};\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface IBaseBroker<TEvents extends {}> {\n\t/**\n\t * Subscribes to the given events\n\t */\n\tsubscribe(events: (keyof TEvents)[]): Promise<void>;\n\t/**\n\t * Unsubscribes from the given events\n\t */\n\tunsubscribe(events: (keyof TEvents)[]): Promise<void>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface IPubSubBroker<TEvents extends {}>\n\textends IBaseBroker<TEvents>,\n\t\tAsyncEventEmitter<ToEventMap<TEvents>> {\n\t/**\n\t * Publishes an event\n\t */\n\tpublish<Event extends keyof TEvents>(event: Event, data: TEvents[Event]): Promise<void>;\n}\n\nexport interface IRPCBroker<TEvents extends Record<string, any[]>, TResponses extends Record<keyof TEvents, any>>\n\textends IBaseBroker<TEvents>,\n\t\tAsyncEventEmitter<ToEventMap<TEvents, TResponses>> {\n\t/**\n\t * Makes an RPC call\n\t */\n\tcall<Event extends keyof TEvents>(\n\t\tevent: Event,\n\t\tdata: TEvents[Event],\n\t\ttimeoutDuration?: number,\n\t): Promise<TResponses[Event]>;\n}\n","import type { Buffer } from 'node:buffer';\nimport type { IPubSubBroker } from '../Broker.js';\nimport { BaseRedisBroker } from './BaseRedis.js';\n\n/**\n * PubSub broker powered by Redis\n *\n * @example\n * ```ts\n * // publisher.js\n * import { PubSubRedisBroker } from '@discordjs/brokers';\n * import Redis from 'ioredis';\n *\n * const broker = new PubSubRedisBroker(new Redis());\n *\n * await broker.publish('test', 'Hello World!');\n * await broker.destroy();\n *\n * // subscriber.js\n * import { PubSubRedisBroker } from '@discordjs/brokers';\n * import Redis from 'ioredis';\n *\n * const broker = new PubSubRedisBroker(new Redis());\n * \tbroker.on('test', ({ data, ack }) => {\n * \tconsole.log(data);\n * \tvoid ack();\n * });\n *\n * await broker.subscribe('subscribers', ['test']);\n * ```\n */\nexport class PubSubRedisBroker<TEvents extends Record<string, any>>\n\textends BaseRedisBroker<TEvents>\n\timplements IPubSubBroker<TEvents>\n{\n\t/**\n\t * {@inheritDoc IPubSubBroker.publish}\n\t */\n\tpublic async publish<Event extends keyof TEvents>(event: Event, data: TEvents[Event]): Promise<void> {\n\t\tawait this.redisClient.xadd(event as string, '*', BaseRedisBroker.STREAM_DATA_KEY, this.options.encode(data));\n\t}\n\n\tprotected emitEvent(id: Buffer, group: string, event: string, data: unknown) {\n\t\tconst payload: { ack(): Promise<void>; data: unknown } = {\n\t\t\tdata,\n\t\t\tack: async () => {\n\t\t\t\tawait this.redisClient.xack(event, group, id);\n\t\t\t},\n\t\t};\n\n\t\t// @ts-expect-error: Intended\n\t\tthis.emit(event, payload);\n\t}\n}\n","import type { Buffer } from 'node:buffer';\nimport { clearTimeout, setTimeout } from 'node:timers';\nimport type Redis from 'ioredis/built/Redis.js';\nimport type { IRPCBroker } from '../Broker.js';\nimport type { RedisBrokerOptions } from './BaseRedis.js';\nimport { BaseRedisBroker, DefaultRedisBrokerOptions } from './BaseRedis.js';\n\ninterface InternalPromise {\n\treject(error: any): void;\n\tresolve(data: any): void;\n\ttimeout: NodeJS.Timeout;\n}\n\n/**\n * Options specific for an RPC Redis broker\n */\nexport interface RPCRedisBrokerOptions extends RedisBrokerOptions {\n\ttimeout?: number;\n}\n\n/**\n * Default values used for the {@link RPCRedisBrokerOptions}\n */\nexport const DefaultRPCRedisBrokerOptions = {\n\t...DefaultRedisBrokerOptions,\n\ttimeout: 5_000,\n} as const satisfies Required<Omit<RPCRedisBrokerOptions, 'group' | 'name'>>;\n\n/**\n * RPC broker powered by Redis\n *\n * @example\n * ```ts\n * // caller.js\n * import { RPCRedisBroker } from '@discordjs/brokers';\n * import Redis from 'ioredis';\n *\n * const broker = new RPCRedisBroker(new Redis());\n *\n * console.log(await broker.call('testcall', 'Hello World!'));\n * await broker.destroy();\n *\n * // responder.js\n * import { RPCRedisBroker } from '@discordjs/brokers';\n * import Redis from 'ioredis';\n *\n * const broker = new RPCRedisBroker(new Redis());\n * broker.on('testcall', ({ data, ack, reply }) => {\n * \tconsole.log('responder', data);\n * \tvoid ack();\n * \tvoid reply(`Echo: ${data}`);\n * });\n *\n * await broker.subscribe('responders', ['testcall']);\n * ```\n */\nexport class RPCRedisBroker<TEvents extends Record<string, any[]>, TResponses extends Record<keyof TEvents, any>>\n\textends BaseRedisBroker<TEvents, TResponses>\n\timplements IRPCBroker<TEvents, TResponses>\n{\n\t/**\n\t * Options this broker is using\n\t */\n\tprotected override readonly options: Required<RPCRedisBrokerOptions>;\n\n\t/**\n\t * @internal\n\t */\n\tprotected readonly promises = new Map<string, InternalPromise>();\n\n\tpublic constructor(redisClient: Redis, options: RPCRedisBrokerOptions) {\n\t\tsuper(redisClient, options);\n\t\tthis.options = { ...DefaultRPCRedisBrokerOptions, ...options };\n\n\t\tthis.streamReadClient.on('messageBuffer', (channel: Buffer, message: Buffer) => {\n\t\t\tconst [, id] = channel.toString().split(':');\n\t\t\tif (id && this.promises.has(id)) {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\t\tconst { resolve, timeout } = this.promises.get(id)!;\n\t\t\t\tresolve(this.options.decode(message));\n\t\t\t\tclearTimeout(timeout);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * {@inheritDoc IRPCBroker.call}\n\t */\n\tpublic async call<Event extends keyof TEvents>(\n\t\tevent: Event,\n\t\tdata: TEvents[Event],\n\t\ttimeoutDuration: number = this.options.timeout,\n\t): Promise<TResponses[Event]> {\n\t\tconst id = await this.redisClient.xadd(\n\t\t\tevent as string,\n\t\t\t'*',\n\t\t\tBaseRedisBroker.STREAM_DATA_KEY,\n\t\t\tthis.options.encode(data),\n\t\t);\n\t\t// This id! assertion is valid. From redis docs:\n\t\t// \"The command returns a Null reply when used with the NOMKSTREAM option and the key doesn't exist.\"\n\t\t// See: https://redis.io/commands/xadd/\n\t\tconst rpcChannel = `${event as string}:${id!}`;\n\n\t\t// Construct the error here for better stack traces\n\t\tconst timedOut = new Error(`timed out after ${timeoutDuration}ms`);\n\n\t\tawait this.streamReadClient.subscribe(rpcChannel);\n\t\treturn new Promise<TResponses[Event]>((resolve, reject) => {\n\t\t\tconst timeout = setTimeout(() => reject(timedOut), timeoutDuration).unref();\n\n\t\t\tthis.promises.set(id!, { resolve, reject, timeout });\n\t\t\t// eslint-disable-next-line promise/prefer-await-to-then\n\t\t}).finally(() => {\n\t\t\tvoid this.streamReadClient.unsubscribe(rpcChannel);\n\t\t\tthis.promises.delete(id!);\n\t\t});\n\t}\n\n\tprotected emitEvent(id: Buffer, event: string, data: unknown) {\n\t\tconst payload: { ack(): Promise<void>; data: unknown; reply(data: unknown): Promise<void> } = {\n\t\t\tdata,\n\t\t\tack: async () => {\n\t\t\t\tawait this.redisClient.xack(event, this.options.group, id);\n\t\t\t},\n\t\t\treply: async (data) => {\n\t\t\t\tawait this.redisClient.publish(`${event}:${id.toString()}`, this.options.encode(data));\n\t\t\t},\n\t\t};\n\n\t\t// @ts-expect-error: Intended\n\t\tthis.emit(event, payload);\n\t}\n}\n"],"mappings":"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","names":["idx","resolve","data"]}
package/dist/index.mjs CHANGED
@@ -9,7 +9,6 @@ var getDirname = /* @__PURE__ */ __name(() => path.dirname(getFilename()), "getD
9
9
  var __dirname = /* @__PURE__ */ getDirname();
10
10
 
11
11
  // src/brokers/redis/BaseRedis.ts
12
- import { randomBytes } from "crypto";
13
12
  import { readFileSync } from "fs";
14
13
  import { resolve } from "path";
15
14
  import { AsyncEventEmitter } from "@vladfrangu/async_event_emitter";
@@ -29,8 +28,9 @@ var DefaultBrokerOptions = {
29
28
  // src/brokers/redis/BaseRedis.ts
30
29
  var DefaultRedisBrokerOptions = {
31
30
  ...DefaultBrokerOptions,
32
- name: randomBytes(20).toString("hex"),
33
31
  maxChunk: 10,
32
+ maxDeliveredTimes: 3,
33
+ messageIdleTime: 3e3,
34
34
  blockTimeout: 5e3
35
35
  };
36
36
  var BaseRedisBroker = class extends AsyncEventEmitter {
@@ -102,7 +102,7 @@ var BaseRedisBroker = class extends AsyncEventEmitter {
102
102
  }
103
103
  }
104
104
  /**
105
- * Begins polling for events, firing them to {@link BaseRedisBroker.listen}
105
+ * Begins polling for events, firing them to {@link BaseRedisBroker.emitEvent}
106
106
  */
107
107
  async listen() {
108
108
  if (this.listening) {
@@ -111,34 +111,16 @@ var BaseRedisBroker = class extends AsyncEventEmitter {
111
111
  this.listening = true;
112
112
  while (this.subscribedEvents.size > 0) {
113
113
  try {
114
- const data = await this.streamReadClient.xreadgroupBuffer(
115
- "GROUP",
116
- this.options.group,
117
- this.options.name,
118
- "COUNT",
119
- String(this.options.maxChunk),
120
- "BLOCK",
121
- String(this.options.blockTimeout),
122
- "STREAMS",
123
- ...this.subscribedEvents,
124
- ...Array.from({ length: this.subscribedEvents.size }, () => ">")
125
- );
114
+ await this.claimAndEmitDeadEvents();
115
+ } catch (error) {
116
+ this.emit("error", error);
117
+ }
118
+ try {
119
+ const data = await this.readGroup(">", this.options.blockTimeout);
126
120
  if (!data) {
127
121
  continue;
128
122
  }
129
- for (const [event, info] of data) {
130
- for (const [id, packet] of info) {
131
- const idx = packet.findIndex((value, idx2) => value.toString("utf8") === "data" && idx2 % 2 === 0);
132
- if (idx < 0) {
133
- continue;
134
- }
135
- const data2 = packet[idx + 1];
136
- if (!data2) {
137
- continue;
138
- }
139
- this.emitEvent(id, this.options.group, event.toString("utf8"), this.options.decode(data2));
140
- }
141
- }
123
+ await this.processMessages(data);
142
124
  } catch (error) {
143
125
  this.emit("error", error);
144
126
  break;
@@ -146,6 +128,81 @@ var BaseRedisBroker = class extends AsyncEventEmitter {
146
128
  }
147
129
  this.listening = false;
148
130
  }
131
+ async readGroup(fromId, block) {
132
+ const data = await this.streamReadClient.xreadgroupBuffer(
133
+ "GROUP",
134
+ this.options.group,
135
+ this.options.name,
136
+ "COUNT",
137
+ String(this.options.maxChunk),
138
+ "BLOCK",
139
+ String(block),
140
+ "STREAMS",
141
+ ...this.subscribedEvents,
142
+ ...Array.from({ length: this.subscribedEvents.size }, () => fromId)
143
+ );
144
+ return data ?? [];
145
+ }
146
+ async processMessages(data) {
147
+ for (const [event, messages] of data) {
148
+ const eventName = event.toString("utf8");
149
+ for (const [id, packet] of messages) {
150
+ const idx = packet.findIndex((value, idx2) => value.toString("utf8") === "data" && idx2 % 2 === 0);
151
+ if (idx < 0) continue;
152
+ const payload = packet[idx + 1];
153
+ if (!payload) continue;
154
+ this.emitEvent(id, this.options.group, eventName, this.options.decode(payload));
155
+ }
156
+ }
157
+ }
158
+ async claimAndEmitDeadEvents() {
159
+ for (const stream of this.subscribedEvents) {
160
+ const pending = await this.streamReadClient.xpending(
161
+ stream,
162
+ this.options.group,
163
+ "-",
164
+ "+",
165
+ this.options.maxChunk
166
+ // See: https://redis.io/docs/latest/commands/xpending/#extended-form-of-xpending
167
+ );
168
+ for (const [id, consumer, idleMs, deliveredTimes] of pending) {
169
+ if (idleMs < this.options.messageIdleTime) {
170
+ continue;
171
+ }
172
+ if (deliveredTimes > this.options.maxDeliveredTimes) {
173
+ await this.streamReadClient.xdel(stream, this.options.group, id);
174
+ continue;
175
+ }
176
+ if (consumer !== this.options.name) {
177
+ const claimed = await this.streamReadClient.xclaimBuffer(
178
+ stream,
179
+ this.options.group,
180
+ this.options.name,
181
+ Math.max(this.options.messageIdleTime, 1),
182
+ id,
183
+ "JUSTID"
184
+ );
185
+ if (!claimed?.length) {
186
+ continue;
187
+ }
188
+ }
189
+ const entries = await this.streamReadClient.xrangeBuffer(stream, id, id);
190
+ if (!entries?.length) {
191
+ continue;
192
+ }
193
+ const [msgId, fields] = entries[0];
194
+ const idx = fields.findIndex((value, idx2) => value.toString("utf8") === "data" && idx2 % 2 === 0);
195
+ if (idx < 0) {
196
+ continue;
197
+ }
198
+ const payload = fields[idx + 1];
199
+ if (!payload) {
200
+ continue;
201
+ }
202
+ this.emitEvent(msgId, this.options.group, stream, this.options.decode(payload));
203
+ }
204
+ }
205
+ }
149
206
  /**
150
207
  * Destroys the broker, closing all connections
151
208
  */
@@ -244,7 +301,7 @@ var RPCRedisBroker = class extends BaseRedisBroker {
244
301
  };
245
302
 
246
303
  // src/index.ts
247
- var version = "2.0.0-dev.1759017751-1c5674d9b";
304
+ var version = "2.0.0-dev.1759579293-cf89260c9";
248
305
  export {
249
306
  BaseRedisBroker,
250
307
  DefaultBrokerOptions,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../node_modules/.pnpm/tsup@8.5.0_@microsoft+api-extractor@7.52.11_@types+node@22.17.2__jiti@2.5.1_postcss@8.5.6_typescript@5.9.2_yaml@2.8.1/node_modules/tsup/assets/esm_shims.js","../src/brokers/redis/BaseRedis.ts","../src/brokers/Broker.ts","../src/brokers/redis/PubSubRedis.ts","../src/brokers/redis/RPCRedis.ts","../src/index.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","import type { Buffer } from 'node:buffer';\nimport { randomBytes } from 'node:crypto';\nimport { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';\nimport type { Redis } from 'ioredis';\nimport { ReplyError } from 'ioredis';\nimport type { BaseBrokerOptions, IBaseBroker, ToEventMap } from '../Broker.js';\nimport { DefaultBrokerOptions } from '../Broker.js';\n\n// For some reason ioredis doesn't have this typed, but it exists\ndeclare module 'ioredis' {\n\tinterface Redis {\n\t\txreadgroupBuffer(...args: (Buffer | string)[]): Promise<[Buffer, [Buffer, Buffer[]][]][] | null>;\n\t}\n}\n\n/**\n * Options specific for a Redis broker\n */\nexport interface RedisBrokerOptions extends BaseBrokerOptions {\n\t/**\n\t * How long to block for messages when polling\n\t */\n\tblockTimeout?: number;\n\n\t/**\n\t * Consumer group name to use for this broker\n\t *\n\t * @see {@link https://redis.io/commands/xreadgroup/}\n\t */\n\tgroup: string;\n\n\t/**\n\t * Max number of messages to poll at once\n\t */\n\tmaxChunk?: number;\n\n\t/**\n\t * Unique consumer name.\n\t *\n\t * @see {@link https://redis.io/commands/xreadgroup/}\n\t */\n\tname?: string;\n}\n\n/**\n * Default broker options for redis\n */\nexport const DefaultRedisBrokerOptions = {\n\t...DefaultBrokerOptions,\n\tname: randomBytes(20).toString('hex'),\n\tmaxChunk: 10,\n\tblockTimeout: 5_000,\n} as const satisfies Required<Omit<RedisBrokerOptions, 'group'>>;\n\n/**\n * Helper class with shared Redis logic\n */\nexport abstract class BaseRedisBroker<\n\t\tTEvents extends Record<string, any[]>,\n\t\tTResponses extends Record<keyof TEvents, any> | undefined = undefined,\n\t>\n\textends AsyncEventEmitter<ToEventMap<TEvents, TResponses>>\n\timplements IBaseBroker<TEvents>\n{\n\t/**\n\t * Used for Redis queues, see the 3rd argument taken by {@link https://redis.io/commands/xadd | xadd}\n\t */\n\tpublic static readonly STREAM_DATA_KEY = 'data' as const;\n\n\t/**\n\t * Options this broker is using\n\t */\n\tprotected readonly options: Required<RedisBrokerOptions>;\n\n\t/**\n\t * Events this broker has subscribed to\n\t */\n\tprotected readonly subscribedEvents = new Set<string>();\n\n\t/**\n\t * Internal copy of the Redis client being used to read incoming payloads\n\t */\n\tprotected readonly streamReadClient: Redis;\n\n\t/**\n\t * Whether this broker is currently polling events\n\t */\n\tprotected listening = false;\n\n\tpublic constructor(\n\t\tprotected readonly redisClient: Redis,\n\t\toptions: RedisBrokerOptions,\n\t) {\n\t\tsuper();\n\t\tthis.options = { ...DefaultRedisBrokerOptions, ...options };\n\t\tredisClient.defineCommand('xcleangroup', {\n\t\t\tnumberOfKeys: 1,\n\t\t\tlua: readFileSync(resolve(__dirname, '..', 'scripts', 'xcleangroup.lua'), 'utf8'),\n\t\t});\n\t\tthis.streamReadClient = redisClient.duplicate();\n\t}\n\n\t/**\n\t * {@inheritDoc IBaseBroker.subscribe}\n\t */\n\tpublic async subscribe(events: (keyof TEvents)[]): Promise<void> {\n\t\tawait Promise.all(\n\t\t\t// @ts-expect-error: Intended\n\t\t\tevents.map(async (event) => {\n\t\t\t\tthis.subscribedEvents.add(event as string);\n\t\t\t\ttry {\n\t\t\t\t\treturn await this.redisClient.xgroup('CREATE', event as string, this.options.group, 0, 'MKSTREAM');\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!(error instanceof ReplyError)) {\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\t\tvoid this.listen();\n\t}\n\n\t/**\n\t * {@inheritDoc IBaseBroker.unsubscribe}\n\t */\n\tpublic async unsubscribe(events: (keyof TEvents)[]): Promise<void> {\n\t\tconst commands: unknown[][] = Array.from({ length: events.length * 2 });\n\t\tfor (let idx = 0; idx < commands.length; idx += 2) {\n\t\t\tconst event = events[idx / 2];\n\t\t\tcommands[idx] = ['xgroup', 'delconsumer', event as string, this.options.group, this.options.name];\n\t\t\tcommands[idx + 1] = ['xcleangroup', event as string, this.options.group];\n\t\t}\n\n\t\tawait this.redisClient.pipeline(commands).exec();\n\n\t\tfor (const event of events) {\n\t\t\tthis.subscribedEvents.delete(event as string);\n\t\t}\n\t}\n\n\t/**\n\t * Begins polling for events, firing them to {@link BaseRedisBroker.listen}\n\t */\n\tprotected async listen(): Promise<void> {\n\t\tif (this.listening) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.listening = true;\n\n\t\twhile (this.subscribedEvents.size > 0) {\n\t\t\ttry {\n\t\t\t\tconst data = await this.streamReadClient.xreadgroupBuffer(\n\t\t\t\t\t'GROUP',\n\t\t\t\t\tthis.options.group,\n\t\t\t\t\tthis.options.name,\n\t\t\t\t\t'COUNT',\n\t\t\t\t\tString(this.options.maxChunk),\n\t\t\t\t\t'BLOCK',\n\t\t\t\t\tString(this.options.blockTimeout),\n\t\t\t\t\t'STREAMS',\n\t\t\t\t\t...this.subscribedEvents,\n\t\t\t\t\t...Array.from({ length: this.subscribedEvents.size }, () => '>'),\n\t\t\t\t);\n\n\t\t\t\tif (!data) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tfor (const [event, info] of data) {\n\t\t\t\t\tfor (const [id, packet] of info) {\n\t\t\t\t\t\tconst idx = packet.findIndex((value, idx) => value.toString('utf8') === 'data' && idx % 2 === 0);\n\t\t\t\t\t\tif (idx < 0) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst data = packet[idx + 1];\n\t\t\t\t\t\tif (!data) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.emitEvent(id, this.options.group, event.toString('utf8'), this.options.decode(data));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// @ts-expect-error: Intended\n\t\t\t\tthis.emit('error', error);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tthis.listening = false;\n\t}\n\n\t/**\n\t * Destroys the broker, closing all connections\n\t */\n\tpublic async destroy() {\n\t\tawait this.unsubscribe([...this.subscribedEvents]);\n\t\tthis.streamReadClient.disconnect();\n\t\tthis.redisClient.disconnect();\n\t}\n\n\t/**\n\t * Handles an incoming Redis event\n\t */\n\tprotected abstract emitEvent(id: Buffer, group: string, event: string, data: unknown): unknown;\n}\n","import { Buffer } from 'node:buffer';\nimport { encode, decode } from '@msgpack/msgpack';\nimport type { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';\n\n/**\n * Base options for a broker implementation\n */\nexport interface BaseBrokerOptions {\n\t/**\n\t * Function to use for decoding messages\n\t */\n\t// eslint-disable-next-line @typescript-eslint/method-signature-style\n\tdecode?: (data: Buffer) => unknown;\n\t/**\n\t * Function to use for encoding messages\n\t */\n\t// eslint-disable-next-line @typescript-eslint/method-signature-style\n\tencode?: (data: unknown) => Buffer;\n}\n\n/**\n * Default broker options\n */\nexport const DefaultBrokerOptions = {\n\tencode: (data): Buffer => {\n\t\tconst encoded = encode(data);\n\t\treturn Buffer.from(encoded.buffer, encoded.byteOffset, encoded.byteLength);\n\t},\n\tdecode: (data): unknown => decode(data),\n} as const satisfies Required<BaseBrokerOptions>;\n\nexport type ToEventMap<\n\tTRecord extends Record<string, any[]>,\n\tTResponses extends Record<keyof TRecord, any> | undefined = undefined,\n> = {\n\t[TKey in keyof TRecord]: [\n\t\tevent: TResponses extends Record<keyof TRecord, any>\n\t\t\t? { ack(): Promise<void>; reply(data: TResponses[TKey]): Promise<void> }\n\t\t\t: { ack(): Promise<void>; data: TRecord[TKey] },\n\t];\n};\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface IBaseBroker<TEvents extends {}> {\n\t/**\n\t * Subscribes to the given events\n\t */\n\tsubscribe(events: (keyof TEvents)[]): Promise<void>;\n\t/**\n\t * Unsubscribes from the given events\n\t */\n\tunsubscribe(events: (keyof TEvents)[]): Promise<void>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface IPubSubBroker<TEvents extends {}>\n\textends IBaseBroker<TEvents>,\n\t\tAsyncEventEmitter<ToEventMap<TEvents>> {\n\t/**\n\t * Publishes an event\n\t */\n\tpublish<Event extends keyof TEvents>(event: Event, data: TEvents[Event]): Promise<void>;\n}\n\nexport interface IRPCBroker<TEvents extends Record<string, any[]>, TResponses extends Record<keyof TEvents, any>>\n\textends IBaseBroker<TEvents>,\n\t\tAsyncEventEmitter<ToEventMap<TEvents, TResponses>> {\n\t/**\n\t * Makes an RPC call\n\t */\n\tcall<Event extends keyof TEvents>(\n\t\tevent: Event,\n\t\tdata: TEvents[Event],\n\t\ttimeoutDuration?: number,\n\t): Promise<TResponses[Event]>;\n}\n","import type { Buffer } from 'node:buffer';\nimport type { IPubSubBroker } from '../Broker.js';\nimport { BaseRedisBroker } from './BaseRedis.js';\n\n/**\n * PubSub broker powered by Redis\n *\n * @example\n * ```ts\n * // publisher.js\n * import { PubSubRedisBroker } from '@discordjs/brokers';\n * import Redis from 'ioredis';\n *\n * const broker = new PubSubRedisBroker(new Redis());\n *\n * await broker.publish('test', 'Hello World!');\n * await broker.destroy();\n *\n * // subscriber.js\n * import { PubSubRedisBroker } from '@discordjs/brokers';\n * import Redis from 'ioredis';\n *\n * const broker = new PubSubRedisBroker(new Redis());\n * \tbroker.on('test', ({ data, ack }) => {\n * \tconsole.log(data);\n * \tvoid ack();\n * });\n *\n * await broker.subscribe('subscribers', ['test']);\n * ```\n */\nexport class PubSubRedisBroker<TEvents extends Record<string, any>>\n\textends BaseRedisBroker<TEvents>\n\timplements IPubSubBroker<TEvents>\n{\n\t/**\n\t * {@inheritDoc IPubSubBroker.publish}\n\t */\n\tpublic async publish<Event extends keyof TEvents>(event: Event, data: TEvents[Event]): Promise<void> {\n\t\tawait this.redisClient.xadd(event as string, '*', BaseRedisBroker.STREAM_DATA_KEY, this.options.encode(data));\n\t}\n\n\tprotected emitEvent(id: Buffer, group: string, event: string, data: unknown) {\n\t\tconst payload: { ack(): Promise<void>; data: unknown } = {\n\t\t\tdata,\n\t\t\tack: async () => {\n\t\t\t\tawait this.redisClient.xack(event, group, id);\n\t\t\t},\n\t\t};\n\n\t\t// @ts-expect-error: Intended\n\t\tthis.emit(event, payload);\n\t}\n}\n","import type { Buffer } from 'node:buffer';\nimport { clearTimeout, setTimeout } from 'node:timers';\nimport type Redis from 'ioredis/built/Redis.js';\nimport type { IRPCBroker } from '../Broker.js';\nimport type { RedisBrokerOptions } from './BaseRedis.js';\nimport { BaseRedisBroker, DefaultRedisBrokerOptions } from './BaseRedis.js';\n\ninterface InternalPromise {\n\treject(error: any): void;\n\tresolve(data: any): void;\n\ttimeout: NodeJS.Timeout;\n}\n\n/**\n * Options specific for an RPC Redis broker\n */\nexport interface RPCRedisBrokerOptions extends RedisBrokerOptions {\n\ttimeout?: number;\n}\n\n/**\n * Default values used for the {@link RPCRedisBrokerOptions}\n */\nexport const DefaultRPCRedisBrokerOptions = {\n\t...DefaultRedisBrokerOptions,\n\ttimeout: 5_000,\n} as const satisfies Required<Omit<RPCRedisBrokerOptions, 'group'>>;\n\n/**\n * RPC broker powered by Redis\n *\n * @example\n * ```ts\n * // caller.js\n * import { RPCRedisBroker } from '@discordjs/brokers';\n * import Redis from 'ioredis';\n *\n * const broker = new RPCRedisBroker(new Redis());\n *\n * console.log(await broker.call('testcall', 'Hello World!'));\n * await broker.destroy();\n *\n * // responder.js\n * import { RPCRedisBroker } from '@discordjs/brokers';\n * import Redis from 'ioredis';\n *\n * const broker = new RPCRedisBroker(new Redis());\n * broker.on('testcall', ({ data, ack, reply }) => {\n * \tconsole.log('responder', data);\n * \tvoid ack();\n * \tvoid reply(`Echo: ${data}`);\n * });\n *\n * await broker.subscribe('responders', ['testcall']);\n * ```\n */\nexport class RPCRedisBroker<TEvents extends Record<string, any[]>, TResponses extends Record<keyof TEvents, any>>\n\textends BaseRedisBroker<TEvents, TResponses>\n\timplements IRPCBroker<TEvents, TResponses>\n{\n\t/**\n\t * Options this broker is using\n\t */\n\tprotected override readonly options: Required<RPCRedisBrokerOptions>;\n\n\t/**\n\t * @internal\n\t */\n\tprotected readonly promises = new Map<string, InternalPromise>();\n\n\tpublic constructor(redisClient: Redis, options: RPCRedisBrokerOptions) {\n\t\tsuper(redisClient, options);\n\t\tthis.options = { ...DefaultRPCRedisBrokerOptions, ...options };\n\n\t\tthis.streamReadClient.on('messageBuffer', (channel: Buffer, message: Buffer) => {\n\t\t\tconst [, id] = channel.toString().split(':');\n\t\t\tif (id && this.promises.has(id)) {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\t\tconst { resolve, timeout } = this.promises.get(id)!;\n\t\t\t\tresolve(this.options.decode(message));\n\t\t\t\tclearTimeout(timeout);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * {@inheritDoc IRPCBroker.call}\n\t */\n\tpublic async call<Event extends keyof TEvents>(\n\t\tevent: Event,\n\t\tdata: TEvents[Event],\n\t\ttimeoutDuration: number = this.options.timeout,\n\t): Promise<TResponses[Event]> {\n\t\tconst id = await this.redisClient.xadd(\n\t\t\tevent as string,\n\t\t\t'*',\n\t\t\tBaseRedisBroker.STREAM_DATA_KEY,\n\t\t\tthis.options.encode(data),\n\t\t);\n\t\t// This id! assertion is valid. From redis docs:\n\t\t// \"The command returns a Null reply when used with the NOMKSTREAM option and the key doesn't exist.\"\n\t\t// See: https://redis.io/commands/xadd/\n\t\tconst rpcChannel = `${event as string}:${id!}`;\n\n\t\t// Construct the error here for better stack traces\n\t\tconst timedOut = new Error(`timed out after ${timeoutDuration}ms`);\n\n\t\tawait this.streamReadClient.subscribe(rpcChannel);\n\t\treturn new Promise<TResponses[Event]>((resolve, reject) => {\n\t\t\tconst timeout = setTimeout(() => reject(timedOut), timeoutDuration).unref();\n\n\t\t\tthis.promises.set(id!, { resolve, reject, timeout });\n\t\t\t// eslint-disable-next-line promise/prefer-await-to-then\n\t\t}).finally(() => {\n\t\t\tvoid this.streamReadClient.unsubscribe(rpcChannel);\n\t\t\tthis.promises.delete(id!);\n\t\t});\n\t}\n\n\tprotected emitEvent(id: Buffer, event: string, data: unknown) {\n\t\tconst payload: { ack(): Promise<void>; data: unknown; reply(data: unknown): Promise<void> } = {\n\t\t\tdata,\n\t\t\tack: async () => {\n\t\t\t\tawait this.redisClient.xack(event, this.options.group, id);\n\t\t\t},\n\t\t\treply: async (data) => {\n\t\t\t\tawait this.redisClient.publish(`${event}:${id.toString()}`, this.options.encode(data));\n\t\t\t},\n\t\t};\n\n\t\t// @ts-expect-error: Intended\n\t\tthis.emit(event, payload);\n\t}\n}\n","export * from './brokers/redis/BaseRedis.js';\nexport * from './brokers/redis/PubSubRedis.js';\nexport * from './brokers/redis/RPCRedis.js';\n\nexport * from './brokers/Broker.js';\n\n/**\n * The {@link https://github.com/discordjs/discord.js/blob/main/packages/brokers#readme | @discordjs/brokers} version\n * that you are currently using.\n *\n * @privateRemarks This needs to explicitly be `string` so it is not typed as a \"const string\" that gets injected by esbuild.\n */\nexport const version = '2.0.0-dev.1759017751-1c5674d9b' as string;\n"],"mappings":"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","names":["Buffer","Buffer","idx","data","resolve","data"]}
1
+ {"version":3,"sources":["../../../node_modules/.pnpm/tsup@8.5.0_@microsoft+api-extractor@7.52.11_@types+node@22.17.2__jiti@2.5.1_postcss@8.5.6_typescript@5.9.2_yaml@2.8.1/node_modules/tsup/assets/esm_shims.js","../src/brokers/redis/BaseRedis.ts","../src/brokers/Broker.ts","../src/brokers/redis/PubSubRedis.ts","../src/brokers/redis/RPCRedis.ts","../src/index.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","import type { Buffer } from 'node:buffer';\nimport { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';\nimport type { Redis } from 'ioredis';\nimport { ReplyError } from 'ioredis';\nimport type { BaseBrokerOptions, IBaseBroker, ToEventMap } from '../Broker.js';\nimport { DefaultBrokerOptions } from '../Broker.js';\n\ntype RedisReadGroupData = [Buffer, [Buffer, Buffer[]][]][];\n\n// For some reason ioredis doesn't have those typed, but they exist\ndeclare module 'ioredis' {\n\tinterface Redis {\n\t\txclaimBuffer(\n\t\t\tkey: Buffer | string,\n\t\t\tgroup: Buffer | string,\n\t\t\tconsumer: Buffer | string,\n\t\t\tminIdleTime: number,\n\t\t\tid: Buffer | string,\n\t\t\t...args: (Buffer | string)[]\n\t\t): Promise<string[]>;\n\t\txreadgroupBuffer(...args: (Buffer | string)[]): Promise<RedisReadGroupData | null>;\n\t}\n}\n\n/**\n * Options specific for a Redis broker\n */\nexport interface RedisBrokerOptions extends BaseBrokerOptions {\n\t/**\n\t * How long to block for messages when polling\n\t */\n\tblockTimeout?: number;\n\t/**\n\t * Consumer group name to use for this broker\n\t *\n\t * @see {@link https://redis.io/commands/xreadgroup/}\n\t */\n\tgroup: string;\n\t/**\n\t * Max number of messages to poll at once\n\t */\n\tmaxChunk?: number;\n\t/**\n\t * How many times a message can be delivered to a consumer before it is considered dead.\n\t * This is used to prevent messages from being stuck in the queue forever if a consumer is\n\t * unable to process them.\n\t */\n\tmaxDeliveredTimes?: number;\n\t/**\n\t * How long a message should be idle for before allowing it to be claimed by another consumer.\n\t * Note that too high of a value can lead to a high delay in processing messages during a service downscale,\n\t * while too low of a value can lead to messages being too eagerly claimed by other consumers during an instance\n\t * restart (which is most likely not actually that problematic)\n\t */\n\tmessageIdleTime?: number;\n\t/**\n\t * Unique consumer name.\n\t *\n\t * @see {@link https://redis.io/commands/xreadgroup/}\n\t */\n\tname: string;\n}\n\n/**\n * Default broker options for redis\n */\nexport const DefaultRedisBrokerOptions = {\n\t...DefaultBrokerOptions,\n\tmaxChunk: 10,\n\tmaxDeliveredTimes: 3,\n\tmessageIdleTime: 3_000,\n\tblockTimeout: 5_000,\n} as const satisfies Required<Omit<RedisBrokerOptions, 'group' | 'name'>>;\n\n/**\n * Helper class with shared Redis logic\n */\nexport abstract class BaseRedisBroker<\n\t\tTEvents extends Record<string, any[]>,\n\t\tTResponses extends Record<keyof TEvents, any> | undefined = undefined,\n\t>\n\textends AsyncEventEmitter<ToEventMap<TEvents, TResponses>>\n\timplements IBaseBroker<TEvents>\n{\n\t/**\n\t * Used for Redis queues, see the 3rd argument taken by {@link https://redis.io/commands/xadd | xadd}\n\t */\n\tpublic static readonly STREAM_DATA_KEY = 'data' as const;\n\n\t/**\n\t * Options this broker is using\n\t */\n\tprotected readonly options: Required<RedisBrokerOptions>;\n\n\t/**\n\t * Events this broker has subscribed to\n\t */\n\tprotected readonly subscribedEvents = new Set<string>();\n\n\t/**\n\t * Internal copy of the Redis client being used to read incoming payloads\n\t */\n\tprotected readonly streamReadClient: Redis;\n\n\t/**\n\t * Whether this broker is currently polling events\n\t */\n\tprotected listening = false;\n\n\tpublic constructor(\n\t\tprotected readonly redisClient: Redis,\n\t\toptions: RedisBrokerOptions,\n\t) {\n\t\tsuper();\n\t\tthis.options = { ...DefaultRedisBrokerOptions, ...options };\n\t\tredisClient.defineCommand('xcleangroup', {\n\t\t\tnumberOfKeys: 1,\n\t\t\tlua: readFileSync(resolve(__dirname, '..', 'scripts', 'xcleangroup.lua'), 'utf8'),\n\t\t});\n\t\tthis.streamReadClient = redisClient.duplicate();\n\t}\n\n\t/**\n\t * {@inheritDoc IBaseBroker.subscribe}\n\t */\n\tpublic async subscribe(events: (keyof TEvents)[]): Promise<void> {\n\t\tawait Promise.all(\n\t\t\t// @ts-expect-error: Intended\n\t\t\tevents.map(async (event) => {\n\t\t\t\tthis.subscribedEvents.add(event as string);\n\t\t\t\ttry {\n\t\t\t\t\treturn await this.redisClient.xgroup('CREATE', event as string, this.options.group, 0, 'MKSTREAM');\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!(error instanceof ReplyError)) {\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\t\tvoid this.listen();\n\t}\n\n\t/**\n\t * {@inheritDoc IBaseBroker.unsubscribe}\n\t */\n\tpublic async unsubscribe(events: (keyof TEvents)[]): Promise<void> {\n\t\tconst commands: unknown[][] = Array.from({ length: events.length * 2 });\n\t\tfor (let idx = 0; idx < commands.length; idx += 2) {\n\t\t\tconst event = events[idx / 2];\n\t\t\tcommands[idx] = ['xgroup', 'delconsumer', event as string, this.options.group, this.options.name];\n\t\t\tcommands[idx + 1] = ['xcleangroup', event as string, this.options.group];\n\t\t}\n\n\t\tawait this.redisClient.pipeline(commands).exec();\n\n\t\tfor (const event of events) {\n\t\t\tthis.subscribedEvents.delete(event as string);\n\t\t}\n\t}\n\n\t/**\n\t * Begins polling for events, firing them to {@link BaseRedisBroker.emitEvent}\n\t */\n\tprotected async listen(): Promise<void> {\n\t\tif (this.listening) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.listening = true;\n\n\t\t// Enter regular polling\n\t\twhile (this.subscribedEvents.size > 0) {\n\t\t\ttry {\n\t\t\t\tawait this.claimAndEmitDeadEvents();\n\t\t\t} catch (error) {\n\t\t\t\t// @ts-expect-error: Intended\n\t\t\t\tthis.emit('error', error);\n\t\t\t\t// We don't break here to keep the loop running even if dead event processing fails\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// As per docs, '>' means \"give me a new message\"\n\t\t\t\tconst data = await this.readGroup('>', this.options.blockTimeout);\n\t\t\t\tif (!data) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tawait this.processMessages(data);\n\t\t\t} catch (error) {\n\t\t\t\t// @ts-expect-error: Intended\n\t\t\t\tthis.emit('error', error);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tthis.listening = false;\n\t}\n\n\tprivate async readGroup(fromId: string, block: number): Promise<RedisReadGroupData> {\n\t\tconst data = await this.streamReadClient.xreadgroupBuffer(\n\t\t\t'GROUP',\n\t\t\tthis.options.group,\n\t\t\tthis.options.name,\n\t\t\t'COUNT',\n\t\t\tString(this.options.maxChunk),\n\t\t\t'BLOCK',\n\t\t\tString(block),\n\t\t\t'STREAMS',\n\t\t\t...this.subscribedEvents,\n\t\t\t...Array.from({ length: this.subscribedEvents.size }, () => fromId),\n\t\t);\n\n\t\treturn data ?? [];\n\t}\n\n\tprivate async processMessages(data: RedisReadGroupData): Promise<void> {\n\t\tfor (const [event, messages] of data) {\n\t\t\tconst eventName = event.toString('utf8');\n\n\t\t\tfor (const [id, packet] of messages) {\n\t\t\t\tconst idx = packet.findIndex((value, idx) => value.toString('utf8') === 'data' && idx % 2 === 0);\n\t\t\t\tif (idx < 0) continue;\n\n\t\t\t\tconst payload = packet[idx + 1];\n\t\t\t\tif (!payload) continue;\n\n\t\t\t\tthis.emitEvent(id, this.options.group, eventName, this.options.decode(payload));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async claimAndEmitDeadEvents(): Promise<void> {\n\t\tfor (const stream of this.subscribedEvents) {\n\t\t\t// Get up to N oldest pending messages (note: a pending message is a message that has been read, but never ACKed)\n\t\t\tconst pending = (await this.streamReadClient.xpending(\n\t\t\t\tstream,\n\t\t\t\tthis.options.group,\n\t\t\t\t'-',\n\t\t\t\t'+',\n\t\t\t\tthis.options.maxChunk,\n\t\t\t\t// See: https://redis.io/docs/latest/commands/xpending/#extended-form-of-xpending\n\t\t\t)) as [id: string, consumer: string, idleMs: number, deliveredTimes: number][];\n\n\t\t\tfor (const [id, consumer, idleMs, deliveredTimes] of pending) {\n\t\t\t\t// Technically xclaim checks for us anyway, but why not avoid an extra call?\n\t\t\t\tif (idleMs < this.options.messageIdleTime) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (deliveredTimes > this.options.maxDeliveredTimes) {\n\t\t\t\t\t// This message is dead. It has repeatedly failed being processed by a consumer.\n\t\t\t\t\tawait this.streamReadClient.xdel(stream, this.options.group, id);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Try to claim the message if we don't already own it (this may fail if another consumer has already claimed it)\n\t\t\t\tif (consumer !== this.options.name) {\n\t\t\t\t\tconst claimed = await this.streamReadClient.xclaimBuffer(\n\t\t\t\t\t\tstream,\n\t\t\t\t\t\tthis.options.group,\n\t\t\t\t\t\tthis.options.name,\n\t\t\t\t\t\tMath.max(this.options.messageIdleTime, 1),\n\t\t\t\t\t\tid,\n\t\t\t\t\t\t'JUSTID',\n\t\t\t\t\t);\n\n\t\t\t\t\t// Another consumer got the message before us\n\t\t\t\t\tif (!claimed?.length) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Fetch message body\n\t\t\t\tconst entries = await this.streamReadClient.xrangeBuffer(stream, id, id);\n\t\t\t\t// No idea how this could happen, frankly!\n\t\t\t\tif (!entries?.length) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst [msgId, fields] = entries[0]!;\n\t\t\t\tconst idx = fields.findIndex((value, idx) => value.toString('utf8') === 'data' && idx % 2 === 0);\n\t\t\t\tif (idx < 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst payload = fields[idx + 1];\n\t\t\t\tif (!payload) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tthis.emitEvent(msgId, this.options.group, stream, this.options.decode(payload));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Destroys the broker, closing all connections\n\t */\n\tpublic async destroy() {\n\t\tawait this.unsubscribe([...this.subscribedEvents]);\n\t\tthis.streamReadClient.disconnect();\n\t\tthis.redisClient.disconnect();\n\t}\n\n\t/**\n\t * Handles an incoming Redis event\n\t */\n\tprotected abstract emitEvent(id: Buffer, group: string, event: string, data: unknown): unknown;\n}\n","import { Buffer } from 'node:buffer';\nimport { encode, decode } from '@msgpack/msgpack';\nimport type { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';\n\n/**\n * Base options for a broker implementation\n */\nexport interface BaseBrokerOptions {\n\t/**\n\t * Function to use for decoding messages\n\t */\n\t// eslint-disable-next-line @typescript-eslint/method-signature-style\n\tdecode?: (data: Buffer) => unknown;\n\t/**\n\t * Function to use for encoding messages\n\t */\n\t// eslint-disable-next-line @typescript-eslint/method-signature-style\n\tencode?: (data: unknown) => Buffer;\n}\n\n/**\n * Default broker options\n */\nexport const DefaultBrokerOptions = {\n\tencode: (data): Buffer => {\n\t\tconst encoded = encode(data);\n\t\treturn Buffer.from(encoded.buffer, encoded.byteOffset, encoded.byteLength);\n\t},\n\tdecode: (data): unknown => decode(data),\n} as const satisfies Required<BaseBrokerOptions>;\n\nexport type ToEventMap<\n\tTRecord extends Record<string, any[]>,\n\tTResponses extends Record<keyof TRecord, any> | undefined = undefined,\n> = {\n\t[TKey in keyof TRecord]: [\n\t\tevent: TResponses extends Record<keyof TRecord, any>\n\t\t\t? { ack(): Promise<void>; reply(data: TResponses[TKey]): Promise<void> }\n\t\t\t: { ack(): Promise<void>; data: TRecord[TKey] },\n\t];\n};\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface IBaseBroker<TEvents extends {}> {\n\t/**\n\t * Subscribes to the given events\n\t */\n\tsubscribe(events: (keyof TEvents)[]): Promise<void>;\n\t/**\n\t * Unsubscribes from the given events\n\t */\n\tunsubscribe(events: (keyof TEvents)[]): Promise<void>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface IPubSubBroker<TEvents extends {}>\n\textends IBaseBroker<TEvents>,\n\t\tAsyncEventEmitter<ToEventMap<TEvents>> {\n\t/**\n\t * Publishes an event\n\t */\n\tpublish<Event extends keyof TEvents>(event: Event, data: TEvents[Event]): Promise<void>;\n}\n\nexport interface IRPCBroker<TEvents extends Record<string, any[]>, TResponses extends Record<keyof TEvents, any>>\n\textends IBaseBroker<TEvents>,\n\t\tAsyncEventEmitter<ToEventMap<TEvents, TResponses>> {\n\t/**\n\t * Makes an RPC call\n\t */\n\tcall<Event extends keyof TEvents>(\n\t\tevent: Event,\n\t\tdata: TEvents[Event],\n\t\ttimeoutDuration?: number,\n\t): Promise<TResponses[Event]>;\n}\n","import type { Buffer } from 'node:buffer';\nimport type { IPubSubBroker } from '../Broker.js';\nimport { BaseRedisBroker } from './BaseRedis.js';\n\n/**\n * PubSub broker powered by Redis\n *\n * @example\n * ```ts\n * // publisher.js\n * import { PubSubRedisBroker } from '@discordjs/brokers';\n * import Redis from 'ioredis';\n *\n * const broker = new PubSubRedisBroker(new Redis());\n *\n * await broker.publish('test', 'Hello World!');\n * await broker.destroy();\n *\n * // subscriber.js\n * import { PubSubRedisBroker } from '@discordjs/brokers';\n * import Redis from 'ioredis';\n *\n * const broker = new PubSubRedisBroker(new Redis());\n * \tbroker.on('test', ({ data, ack }) => {\n * \tconsole.log(data);\n * \tvoid ack();\n * });\n *\n * await broker.subscribe('subscribers', ['test']);\n * ```\n */\nexport class PubSubRedisBroker<TEvents extends Record<string, any>>\n\textends BaseRedisBroker<TEvents>\n\timplements IPubSubBroker<TEvents>\n{\n\t/**\n\t * {@inheritDoc IPubSubBroker.publish}\n\t */\n\tpublic async publish<Event extends keyof TEvents>(event: Event, data: TEvents[Event]): Promise<void> {\n\t\tawait this.redisClient.xadd(event as string, '*', BaseRedisBroker.STREAM_DATA_KEY, this.options.encode(data));\n\t}\n\n\tprotected emitEvent(id: Buffer, group: string, event: string, data: unknown) {\n\t\tconst payload: { ack(): Promise<void>; data: unknown } = {\n\t\t\tdata,\n\t\t\tack: async () => {\n\t\t\t\tawait this.redisClient.xack(event, group, id);\n\t\t\t},\n\t\t};\n\n\t\t// @ts-expect-error: Intended\n\t\tthis.emit(event, payload);\n\t}\n}\n","import type { Buffer } from 'node:buffer';\nimport { clearTimeout, setTimeout } from 'node:timers';\nimport type Redis from 'ioredis/built/Redis.js';\nimport type { IRPCBroker } from '../Broker.js';\nimport type { RedisBrokerOptions } from './BaseRedis.js';\nimport { BaseRedisBroker, DefaultRedisBrokerOptions } from './BaseRedis.js';\n\ninterface InternalPromise {\n\treject(error: any): void;\n\tresolve(data: any): void;\n\ttimeout: NodeJS.Timeout;\n}\n\n/**\n * Options specific for an RPC Redis broker\n */\nexport interface RPCRedisBrokerOptions extends RedisBrokerOptions {\n\ttimeout?: number;\n}\n\n/**\n * Default values used for the {@link RPCRedisBrokerOptions}\n */\nexport const DefaultRPCRedisBrokerOptions = {\n\t...DefaultRedisBrokerOptions,\n\ttimeout: 5_000,\n} as const satisfies Required<Omit<RPCRedisBrokerOptions, 'group' | 'name'>>;\n\n/**\n * RPC broker powered by Redis\n *\n * @example\n * ```ts\n * // caller.js\n * import { RPCRedisBroker } from '@discordjs/brokers';\n * import Redis from 'ioredis';\n *\n * const broker = new RPCRedisBroker(new Redis());\n *\n * console.log(await broker.call('testcall', 'Hello World!'));\n * await broker.destroy();\n *\n * // responder.js\n * import { RPCRedisBroker } from '@discordjs/brokers';\n * import Redis from 'ioredis';\n *\n * const broker = new RPCRedisBroker(new Redis());\n * broker.on('testcall', ({ data, ack, reply }) => {\n * \tconsole.log('responder', data);\n * \tvoid ack();\n * \tvoid reply(`Echo: ${data}`);\n * });\n *\n * await broker.subscribe('responders', ['testcall']);\n * ```\n */\nexport class RPCRedisBroker<TEvents extends Record<string, any[]>, TResponses extends Record<keyof TEvents, any>>\n\textends BaseRedisBroker<TEvents, TResponses>\n\timplements IRPCBroker<TEvents, TResponses>\n{\n\t/**\n\t * Options this broker is using\n\t */\n\tprotected override readonly options: Required<RPCRedisBrokerOptions>;\n\n\t/**\n\t * @internal\n\t */\n\tprotected readonly promises = new Map<string, InternalPromise>();\n\n\tpublic constructor(redisClient: Redis, options: RPCRedisBrokerOptions) {\n\t\tsuper(redisClient, options);\n\t\tthis.options = { ...DefaultRPCRedisBrokerOptions, ...options };\n\n\t\tthis.streamReadClient.on('messageBuffer', (channel: Buffer, message: Buffer) => {\n\t\t\tconst [, id] = channel.toString().split(':');\n\t\t\tif (id && this.promises.has(id)) {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\t\t\tconst { resolve, timeout } = this.promises.get(id)!;\n\t\t\t\tresolve(this.options.decode(message));\n\t\t\t\tclearTimeout(timeout);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * {@inheritDoc IRPCBroker.call}\n\t */\n\tpublic async call<Event extends keyof TEvents>(\n\t\tevent: Event,\n\t\tdata: TEvents[Event],\n\t\ttimeoutDuration: number = this.options.timeout,\n\t): Promise<TResponses[Event]> {\n\t\tconst id = await this.redisClient.xadd(\n\t\t\tevent as string,\n\t\t\t'*',\n\t\t\tBaseRedisBroker.STREAM_DATA_KEY,\n\t\t\tthis.options.encode(data),\n\t\t);\n\t\t// This id! assertion is valid. From redis docs:\n\t\t// \"The command returns a Null reply when used with the NOMKSTREAM option and the key doesn't exist.\"\n\t\t// See: https://redis.io/commands/xadd/\n\t\tconst rpcChannel = `${event as string}:${id!}`;\n\n\t\t// Construct the error here for better stack traces\n\t\tconst timedOut = new Error(`timed out after ${timeoutDuration}ms`);\n\n\t\tawait this.streamReadClient.subscribe(rpcChannel);\n\t\treturn new Promise<TResponses[Event]>((resolve, reject) => {\n\t\t\tconst timeout = setTimeout(() => reject(timedOut), timeoutDuration).unref();\n\n\t\t\tthis.promises.set(id!, { resolve, reject, timeout });\n\t\t\t// eslint-disable-next-line promise/prefer-await-to-then\n\t\t}).finally(() => {\n\t\t\tvoid this.streamReadClient.unsubscribe(rpcChannel);\n\t\t\tthis.promises.delete(id!);\n\t\t});\n\t}\n\n\tprotected emitEvent(id: Buffer, event: string, data: unknown) {\n\t\tconst payload: { ack(): Promise<void>; data: unknown; reply(data: unknown): Promise<void> } = {\n\t\t\tdata,\n\t\t\tack: async () => {\n\t\t\t\tawait this.redisClient.xack(event, this.options.group, id);\n\t\t\t},\n\t\t\treply: async (data) => {\n\t\t\t\tawait this.redisClient.publish(`${event}:${id.toString()}`, this.options.encode(data));\n\t\t\t},\n\t\t};\n\n\t\t// @ts-expect-error: Intended\n\t\tthis.emit(event, payload);\n\t}\n}\n","export * from './brokers/redis/BaseRedis.js';\nexport * from './brokers/redis/PubSubRedis.js';\nexport * from './brokers/redis/RPCRedis.js';\n\nexport * from './brokers/Broker.js';\n\n/**\n * The {@link https://github.com/discordjs/discord.js/blob/main/packages/brokers#readme | @discordjs/brokers} version\n * that you are currently using.\n *\n * @privateRemarks This needs to explicitly be `string` so it is not typed as a \"const string\" that gets injected by esbuild.\n */\nexport const version = '2.0.0-dev.1759579293-cf89260c9' as string;\n"],"mappings":"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","names":["Buffer","Buffer","idx","resolve","data"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@discordjs/brokers",
4
- "version": "2.0.0-dev.1759017751-1c5674d9b",
4
+ "version": "2.0.0-dev.1759579293-cf89260c9",
5
5
  "description": "Powerful set of message brokers",
6
6
  "exports": {
7
7
  ".": {