@orpc/publisher 2.0.0-beta.4 → 2.0.0-beta.40

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.
@@ -0,0 +1,138 @@
1
+ import { Public, ThrowableError } from '@orpc/shared';
2
+ import { Publisher, PublisherOptions, PublisherSubscribeListenerOptions } from '../index.js';
3
+ import { RPCJsonSerializer } from '@orpc/client';
4
+
5
+ /**
6
+ * Options shared by every Redis-backed publisher adapter.
7
+ *
8
+ * @see {@link https://orpc.dev/docs/helpers/publisher#adapters | Publisher Helpers - Adapters}
9
+ */
10
+ interface BaseRedisPublisherOptions extends PublisherOptions {
11
+ /**
12
+ * The prefix to use for Redis keys.
13
+ *
14
+ * @default ''
15
+ */
16
+ prefix?: string;
17
+ /**
18
+ * Serializer for serialize and deserialize payloads.
19
+ *
20
+ * @default RPCJsonSerializer
21
+ */
22
+ serializer?: undefined | Public<RPCJsonSerializer>;
23
+ /**
24
+ * Configuration for event resume support.
25
+ *
26
+ * When enabled, published events are temporarily stored so new
27
+ * subscribers can resume from a previous position using `lastEventId`.
28
+ *
29
+ * @default { enabled: false }
30
+ */
31
+ resume?: {
32
+ /**
33
+ * Whether event resume support is enabled.
34
+ *
35
+ * When enabled, published events are temporarily stored so new
36
+ * subscribers can resume from a previous position using `lastEventId`.
37
+ *
38
+ * @default false
39
+ */
40
+ enabled: boolean;
41
+ /**
42
+ * How long (in seconds) to retain events for resume.
43
+ *
44
+ * Expired events are cleaned up lazily for performance reasons, so
45
+ * some events may remain available slightly longer than this period.
46
+ *
47
+ * @default 300 (5 min)
48
+ */
49
+ seconds?: number;
50
+ };
51
+ }
52
+ /**
53
+ * An entry of the Redis Stream that stores events for resume.
54
+ *
55
+ * @see {@link https://orpc.dev/docs/helpers/publisher#adapters | Publisher Helpers - Adapters}
56
+ */
57
+ interface RedisStreamEntry {
58
+ /**
59
+ * The entry ID assigned by Redis, which doubles as the event ID.
60
+ */
61
+ id: string;
62
+ /**
63
+ * The `data` field, as JSON text or already parsed by clients that deserialize automatically.
64
+ */
65
+ data: unknown;
66
+ }
67
+ /**
68
+ * Trimming to apply to a Redis Stream while adding an entry.
69
+ *
70
+ * @see {@link https://orpc.dev/docs/helpers/publisher#adapters | Publisher Helpers - Adapters}
71
+ */
72
+ interface RedisStreamTrimOptions {
73
+ /**
74
+ * Entries with an ID lower than this one are removed (`XTRIM key MINID minId`).
75
+ */
76
+ minId: string;
77
+ /**
78
+ * Whether trimming is exact (`=`) or approximate (`~`).
79
+ */
80
+ exactness: '~' | '=';
81
+ /**
82
+ * Time to live to set on the stream key, in seconds (`EXPIRE key seconds`).
83
+ */
84
+ expireSeconds: number;
85
+ }
86
+ /**
87
+ * Base class for Redis-backed publisher adapters. It owns the key naming, the
88
+ * message format, and the resume logic, so every adapter built on it can exchange
89
+ * events with the others regardless of the Redis client in use.
90
+ *
91
+ * Extend it and implement the abstract methods to support another Redis client.
92
+ *
93
+ * @see {@link https://orpc.dev/docs/helpers/publisher#adapters | Publisher Helpers - Adapters}
94
+ */
95
+ declare abstract class BaseRedisPublisher<T extends Record<string, object>> extends Publisher<T> {
96
+ protected readonly prefix: string;
97
+ protected readonly serializer: Public<RPCJsonSerializer>;
98
+ protected readonly resumeEnabled: boolean;
99
+ protected readonly resumeSeconds: number;
100
+ /**
101
+ * Tests switch this to `=` for deterministic trimming.
102
+ */
103
+ protected readonly xTrimExactness: '~' | '=';
104
+ private readonly lastTrimTimes;
105
+ constructor({ prefix, serializer, resume, ...options }?: BaseRedisPublisherOptions);
106
+ /**
107
+ * Sends a message to a Pub/Sub channel (`PUBLISH channel message`).
108
+ */
109
+ protected abstract publishMessage(channel: string, message: string): Promise<void>;
110
+ /**
111
+ * Listens for messages on a Pub/Sub channel (`SUBSCRIBE channel`) and resolves
112
+ * with a function that stops listening (`UNSUBSCRIBE channel`).
113
+ *
114
+ * Messages reach the listener as JSON text, or already parsed by clients that
115
+ * deserialize automatically. The listener may be invoked as soon as the subscription is established, even
116
+ * before the returned promise settles. When the promise rejects, nothing may be
117
+ * left subscribed. `onError` reports failures of the established subscription,
118
+ * such as a lost connection.
119
+ */
120
+ protected abstract subscribeChannel(channel: string, listener: (message: unknown) => void, onError?: (error: ThrowableError) => void): Promise<() => Promise<void>>;
121
+ /**
122
+ * Appends an entry to a stream (`XADD key * data <data>`) and resolves with its ID.
123
+ * When `trim` is provided, also trims the stream and refreshes its TTL,
124
+ * preferably within the same round trip.
125
+ */
126
+ protected abstract addStreamEntry(key: string, data: string, trim?: RedisStreamTrimOptions): Promise<string>;
127
+ /**
128
+ * Reads the entries with an ID greater than `lastId` (`XREAD STREAMS key lastId`), oldest first.
129
+ */
130
+ protected abstract readStreamEntries(key: string, lastId: string): Promise<RedisStreamEntry[]>;
131
+ publish<K extends keyof T & string>(event: K, payload: T[K]): Promise<void>;
132
+ protected subscribeListener<K extends keyof T & string>(event: K, listener: (payload: T[K]) => void, { lastEventId, onError }?: PublisherSubscribeListenerOptions): Promise<() => Promise<void>>;
133
+ private serializePayload;
134
+ private deserializePayload;
135
+ }
136
+
137
+ export { BaseRedisPublisher };
138
+ export type { BaseRedisPublisherOptions, RedisStreamEntry, RedisStreamTrimOptions };
@@ -0,0 +1,104 @@
1
+ import { RPCJsonSerializer } from '@orpc/client';
2
+ import { stringifyJSON, once, parseEmptyableJSON } from '@orpc/shared';
3
+ import { unwrapEvent, withEventMeta, getEventMeta } from '@standard-server/core';
4
+ import { P as Publisher } from '../shared/publisher.BSQf8ybR.mjs';
5
+
6
+ class BaseRedisPublisher extends Publisher {
7
+ prefix;
8
+ serializer;
9
+ resumeEnabled;
10
+ resumeSeconds;
11
+ /**
12
+ * Tests switch this to `=` for deterministic trimming.
13
+ */
14
+ xTrimExactness = "~";
15
+ lastTrimTimes = /* @__PURE__ */ new Map();
16
+ constructor({ prefix, serializer, resume, ...options } = {}) {
17
+ super(options);
18
+ this.prefix = prefix ?? "";
19
+ this.serializer = serializer ?? new RPCJsonSerializer();
20
+ this.resumeEnabled = resume?.enabled ?? false;
21
+ this.resumeSeconds = resume?.seconds ?? 300;
22
+ }
23
+ async publish(event, payload) {
24
+ const channel = `${this.prefix}${event}`;
25
+ const data = this.serializePayload(payload);
26
+ let id;
27
+ if (this.resumeEnabled) {
28
+ const now = Date.now();
29
+ const windowMs = this.resumeSeconds * 1e3;
30
+ for (const [trimmedChannel, trimTime] of this.lastTrimTimes) {
31
+ if (trimTime + windowMs < now) {
32
+ this.lastTrimTimes.delete(trimmedChannel);
33
+ }
34
+ }
35
+ if (this.lastTrimTimes.has(channel)) {
36
+ id = await this.addStreamEntry(channel, stringifyJSON(data));
37
+ } else {
38
+ this.lastTrimTimes.set(channel, now);
39
+ id = await this.addStreamEntry(channel, stringifyJSON(data), {
40
+ minId: `${now - windowMs}-0`,
41
+ exactness: this.xTrimExactness,
42
+ // 2x so entries added late in a window outlive it until the next trim refreshes the TTL.
43
+ expireSeconds: this.resumeSeconds * 2
44
+ });
45
+ }
46
+ }
47
+ await this.publishMessage(channel, stringifyJSON({ data, id }));
48
+ }
49
+ async subscribeListener(event, listener, { lastEventId, onError } = {}) {
50
+ const channel = `${this.prefix}${event}`;
51
+ let pendingPayloads = [];
52
+ const resumedIds = /* @__PURE__ */ new Set();
53
+ const deduplicatingListener = (payload) => {
54
+ if (pendingPayloads) {
55
+ pendingPayloads.push(payload);
56
+ return;
57
+ }
58
+ const id = getEventMeta(payload)?.id;
59
+ if (id === void 0 || !resumedIds.has(id)) {
60
+ listener(payload);
61
+ }
62
+ };
63
+ const messageListener = (message) => {
64
+ try {
65
+ const { id, data } = parseIfText(message);
66
+ deduplicatingListener(this.deserializePayload(id, data));
67
+ } catch (error) {
68
+ onError?.(error);
69
+ }
70
+ };
71
+ const unsubscribe = await this.subscribeChannel(channel, messageListener, onError);
72
+ try {
73
+ if (this.resumeEnabled && lastEventId !== void 0) {
74
+ for (const { id, data } of await this.readStreamEntries(channel, lastEventId)) {
75
+ resumedIds.add(id);
76
+ listener(this.deserializePayload(id, parseIfText(data)));
77
+ }
78
+ }
79
+ const pending = pendingPayloads;
80
+ pendingPayloads = void 0;
81
+ pending.forEach(deduplicatingListener);
82
+ } catch (error) {
83
+ await unsubscribe();
84
+ throw error;
85
+ }
86
+ return once(unsubscribe);
87
+ }
88
+ serializePayload(payload) {
89
+ const [original, meta] = unwrapEvent(payload);
90
+ const { json, meta: jsonMeta } = this.serializer.serialize(original);
91
+ return { payload: { json, meta: jsonMeta }, meta };
92
+ }
93
+ deserializePayload(id, { payload, meta }) {
94
+ return withEventMeta(
95
+ this.serializer.deserialize(payload),
96
+ id === void 0 ? { ...meta } : { ...meta, id }
97
+ );
98
+ }
99
+ }
100
+ function parseIfText(value) {
101
+ return typeof value === "string" ? parseEmptyableJSON(value) : value;
102
+ }
103
+
104
+ export { BaseRedisPublisher };
@@ -3,16 +3,16 @@ import '@orpc/shared';
3
3
 
4
4
  interface MemoryPublisherOptions extends PublisherOptions {
5
5
  /**
6
- * Configuration for event replay support.
6
+ * Configuration for event resume support.
7
7
  *
8
8
  * When enabled, published events are temporarily stored so new
9
9
  * subscribers can resume from a previous position using `lastEventId`.
10
10
  *
11
11
  * @default { enabled: false }
12
12
  */
13
- replay?: {
13
+ resume?: {
14
14
  /**
15
- * Whether event replay support is enabled.
15
+ * Whether event resume support is enabled.
16
16
  *
17
17
  * When enabled, published events are temporarily stored so new
18
18
  * subscribers can resume from a previous position using `lastEventId`.
@@ -21,7 +21,7 @@ interface MemoryPublisherOptions extends PublisherOptions {
21
21
  */
22
22
  enabled: boolean;
23
23
  /**
24
- * How long (in seconds) to retain events for replay.
24
+ * How long (in seconds) to retain events for resume.
25
25
  *
26
26
  * Expired events are cleaned up lazily for performance reasons, so
27
27
  * some events may remain available slightly longer than this period.
@@ -31,13 +31,19 @@ interface MemoryPublisherOptions extends PublisherOptions {
31
31
  seconds?: number;
32
32
  };
33
33
  }
34
+ /**
35
+ * Publisher adapter backed by in-memory storage, with optional resume support.
36
+ * Events are delivered to subscribers within the same process only.
37
+ *
38
+ * @see {@link https://orpc.dev/docs/helpers/publisher#adapters | Publisher Helpers - Adapters}
39
+ */
34
40
  declare class MemoryPublisher<T extends Record<string, object>> extends Publisher<T> {
35
41
  private readonly listenersMap;
36
42
  private readonly idGenerator;
37
43
  private readonly eventsMap;
38
- private readonly replayEnabled;
39
- private readonly replaySeconds;
40
- constructor({ replay, ...options }?: MemoryPublisherOptions);
44
+ private readonly resumeEnabled;
45
+ private readonly resumeSeconds;
46
+ constructor({ resume, ...options }?: MemoryPublisherOptions);
41
47
  publish<K extends keyof T & string>(event: K, payload: T[K]): Promise<void>;
42
48
  protected subscribeListener<K extends keyof T & string>(event: K, listener: (payload: T[K]) => void, options?: PublisherSubscribeListenerOptions): Promise<() => Promise<void>>;
43
49
  private lastCleanupTime;
@@ -3,16 +3,16 @@ import '@orpc/shared';
3
3
 
4
4
  interface MemoryPublisherOptions extends PublisherOptions {
5
5
  /**
6
- * Configuration for event replay support.
6
+ * Configuration for event resume support.
7
7
  *
8
8
  * When enabled, published events are temporarily stored so new
9
9
  * subscribers can resume from a previous position using `lastEventId`.
10
10
  *
11
11
  * @default { enabled: false }
12
12
  */
13
- replay?: {
13
+ resume?: {
14
14
  /**
15
- * Whether event replay support is enabled.
15
+ * Whether event resume support is enabled.
16
16
  *
17
17
  * When enabled, published events are temporarily stored so new
18
18
  * subscribers can resume from a previous position using `lastEventId`.
@@ -21,7 +21,7 @@ interface MemoryPublisherOptions extends PublisherOptions {
21
21
  */
22
22
  enabled: boolean;
23
23
  /**
24
- * How long (in seconds) to retain events for replay.
24
+ * How long (in seconds) to retain events for resume.
25
25
  *
26
26
  * Expired events are cleaned up lazily for performance reasons, so
27
27
  * some events may remain available slightly longer than this period.
@@ -31,13 +31,19 @@ interface MemoryPublisherOptions extends PublisherOptions {
31
31
  seconds?: number;
32
32
  };
33
33
  }
34
+ /**
35
+ * Publisher adapter backed by in-memory storage, with optional resume support.
36
+ * Events are delivered to subscribers within the same process only.
37
+ *
38
+ * @see {@link https://orpc.dev/docs/helpers/publisher#adapters | Publisher Helpers - Adapters}
39
+ */
34
40
  declare class MemoryPublisher<T extends Record<string, object>> extends Publisher<T> {
35
41
  private readonly listenersMap;
36
42
  private readonly idGenerator;
37
43
  private readonly eventsMap;
38
- private readonly replayEnabled;
39
- private readonly replaySeconds;
40
- constructor({ replay, ...options }?: MemoryPublisherOptions);
44
+ private readonly resumeEnabled;
45
+ private readonly resumeSeconds;
46
+ constructor({ resume, ...options }?: MemoryPublisherOptions);
41
47
  publish<K extends keyof T & string>(event: K, payload: T[K]): Promise<void>;
42
48
  protected subscribeListener<K extends keyof T & string>(event: K, listener: (payload: T[K]) => void, options?: PublisherSubscribeListenerOptions): Promise<() => Promise<void>>;
43
49
  private lastCleanupTime;
@@ -1,22 +1,22 @@
1
1
  import { SequentialIdGenerator, once, compareSequentialIds } from '@orpc/shared';
2
- import { unwrapEvent, withEventMeta, getEventMeta } from '@standardserver/core';
2
+ import { unwrapEvent, withEventMeta, getEventMeta } from '@standard-server/core';
3
3
  import { P as Publisher } from '../shared/publisher.BSQf8ybR.mjs';
4
4
 
5
5
  class MemoryPublisher extends Publisher {
6
6
  listenersMap = /* @__PURE__ */ new Map();
7
7
  idGenerator = new SequentialIdGenerator();
8
8
  eventsMap = /* @__PURE__ */ new Map();
9
- replayEnabled;
10
- replaySeconds;
11
- constructor({ replay, ...options } = {}) {
9
+ resumeEnabled;
10
+ resumeSeconds;
11
+ constructor({ resume, ...options } = {}) {
12
12
  super(options);
13
- this.replayEnabled = replay?.enabled ?? false;
14
- this.replaySeconds = replay?.seconds ?? 300;
13
+ this.resumeEnabled = resume?.enabled ?? false;
14
+ this.resumeSeconds = resume?.seconds ?? 300;
15
15
  }
16
16
  async publish(event, payload) {
17
17
  this.cleanup();
18
- if (this.replayEnabled) {
19
- const expiresAt = Date.now() + this.replaySeconds * 1e3;
18
+ if (this.resumeEnabled) {
19
+ const expiresAt = Date.now() + this.resumeSeconds * 1e3;
20
20
  let bucket = this.eventsMap.get(event);
21
21
  if (!bucket) {
22
22
  this.eventsMap.set(event, bucket = []);
@@ -30,10 +30,10 @@ class MemoryPublisher extends Publisher {
30
30
  }
31
31
  async subscribeListener(event, listener, options) {
32
32
  this.cleanup();
33
- if (this.replayEnabled && options?.lastEventId !== void 0) {
33
+ if (this.resumeEnabled && options?.lastEventId !== void 0) {
34
34
  const bucket = this.eventsMap.get(event);
35
35
  if (bucket?.length) {
36
- const startIdx = findReplayStartIndex(bucket, options.lastEventId);
36
+ const startIdx = findResumeStartIndex(bucket, options.lastEventId);
37
37
  for (let i = startIdx; i < bucket.length; i++) {
38
38
  listener(bucket[i].payload);
39
39
  }
@@ -53,11 +53,11 @@ class MemoryPublisher extends Publisher {
53
53
  }
54
54
  lastCleanupTime = 0;
55
55
  cleanup() {
56
- if (!this.replayEnabled) {
56
+ if (!this.resumeEnabled) {
57
57
  return;
58
58
  }
59
59
  const now = Date.now();
60
- if (now - this.lastCleanupTime < this.replaySeconds * 1e3) {
60
+ if (now - this.lastCleanupTime < this.resumeSeconds * 1e3) {
61
61
  return;
62
62
  }
63
63
  this.lastCleanupTime = now;
@@ -69,7 +69,7 @@ class MemoryPublisher extends Publisher {
69
69
  }
70
70
  }
71
71
  }
72
- function findReplayStartIndex(bucket, lastEventId) {
72
+ function findResumeStartIndex(bucket, lastEventId) {
73
73
  let lo = 0;
74
74
  let hi = bucket.length;
75
75
  while (lo < hi) {
@@ -1,9 +1,10 @@
1
1
  import { RedisClientType } from 'redis';
2
- import { Publisher, PublisherOptions, PublisherSubscribeListenerOptions } from '../index.mjs';
3
- import { RPCSerializer } from '@orpc/client';
2
+ import { BaseRedisPublisher, BaseRedisPublisherOptions, RedisStreamTrimOptions, RedisStreamEntry } from './base-redis.mjs';
4
3
  import '@orpc/shared';
4
+ import '../index.mjs';
5
+ import '@orpc/client';
5
6
 
6
- interface RedisPublisherOptions extends PublisherOptions {
7
+ interface RedisPublisherOptions extends BaseRedisPublisherOptions {
7
8
  /**
8
9
  * Redis subscriber instance.
9
10
  * Pub/Sub takes over the connection, so a client with subscriptions
@@ -12,63 +13,21 @@ interface RedisPublisherOptions extends PublisherOptions {
12
13
  * @default redis.duplicate()
13
14
  */
14
15
  subscriber?: undefined | RedisClientType<any, any, any, any, any>;
15
- /**
16
- * The prefix to use for Redis keys.
17
- */
18
- prefix?: string;
19
- /**
20
- * Serializer for serialize and deserialize payloads.
21
- *
22
- * @default RPCSerializer
23
- */
24
- serializer?: undefined | Pick<RPCSerializer, keyof RPCSerializer>;
25
- /**
26
- * Configuration for event replay support.
27
- *
28
- * When enabled, published events are temporarily stored so new
29
- * subscribers can resume from a previous position using `lastEventId`.
30
- *
31
- * @default { enabled: false }
32
- */
33
- replay?: {
34
- /**
35
- * Whether event replay support is enabled.
36
- *
37
- * When enabled, published events are temporarily stored so new
38
- * subscribers can resume from a previous position using `lastEventId`.
39
- *
40
- * @default false
41
- */
42
- enabled: boolean;
43
- /**
44
- * How long (in seconds) to retain events for replay.
45
- *
46
- * Expired events are cleaned up lazily for performance reasons, so
47
- * some events may remain available slightly longer than this period.
48
- *
49
- * @default 300 (5 min)
50
- */
51
- seconds?: number;
52
- };
53
16
  }
54
- declare class RedisPublisher<T extends Record<string, object>> extends Publisher<T> {
17
+ /**
18
+ * Publisher adapter for Redis. Distributes events across processes via
19
+ * Redis Pub/Sub, with optional resume support backed by Redis Streams.
20
+ *
21
+ * @see {@link https://orpc.dev/docs/helpers/publisher#adapters | Publisher Helpers - Adapters}
22
+ */
23
+ declare class RedisPublisher<T extends Record<string, object>> extends BaseRedisPublisher<T> {
55
24
  private readonly redis;
56
25
  private readonly subscriber;
57
- private readonly prefix;
58
- private readonly serializer;
59
- private readonly replayEnabled;
60
- private readonly replaySeconds;
61
- /**
62
- * The exactness of the `XTRIM` command.
63
- * Used for testing purpose.
64
- */
65
- private readonly xTrimExactness;
66
- constructor(redis: RedisClientType<any, any, any, any, any>, options?: RedisPublisherOptions);
67
- private readonly firstPublishTimeMap;
68
- publish<K extends keyof T & string>(event: K, payload: T[K]): Promise<void>;
69
- protected subscribeListener<K extends keyof T & string>(event: K, originalListener: (payload: T[K]) => void, { lastEventId, onError }?: PublisherSubscribeListenerOptions): Promise<() => Promise<void>>;
70
- private serializePayload;
71
- private deserializePayload;
26
+ constructor(redis: RedisClientType<any, any, any, any, any>, { subscriber, ...options }?: RedisPublisherOptions);
27
+ protected publishMessage(channel: string, message: string): Promise<void>;
28
+ protected subscribeChannel(channel: string, listener: (message: unknown) => void): Promise<() => Promise<void>>;
29
+ protected addStreamEntry(key: string, data: string, trim?: RedisStreamTrimOptions): Promise<string>;
30
+ protected readStreamEntries(key: string, lastId: string): Promise<RedisStreamEntry[]>;
72
31
  }
73
32
 
74
33
  export { RedisPublisher };
@@ -1,9 +1,10 @@
1
1
  import { RedisClientType } from 'redis';
2
- import { Publisher, PublisherOptions, PublisherSubscribeListenerOptions } from '../index.js';
3
- import { RPCSerializer } from '@orpc/client';
2
+ import { BaseRedisPublisher, BaseRedisPublisherOptions, RedisStreamTrimOptions, RedisStreamEntry } from './base-redis.js';
4
3
  import '@orpc/shared';
4
+ import '../index.js';
5
+ import '@orpc/client';
5
6
 
6
- interface RedisPublisherOptions extends PublisherOptions {
7
+ interface RedisPublisherOptions extends BaseRedisPublisherOptions {
7
8
  /**
8
9
  * Redis subscriber instance.
9
10
  * Pub/Sub takes over the connection, so a client with subscriptions
@@ -12,63 +13,21 @@ interface RedisPublisherOptions extends PublisherOptions {
12
13
  * @default redis.duplicate()
13
14
  */
14
15
  subscriber?: undefined | RedisClientType<any, any, any, any, any>;
15
- /**
16
- * The prefix to use for Redis keys.
17
- */
18
- prefix?: string;
19
- /**
20
- * Serializer for serialize and deserialize payloads.
21
- *
22
- * @default RPCSerializer
23
- */
24
- serializer?: undefined | Pick<RPCSerializer, keyof RPCSerializer>;
25
- /**
26
- * Configuration for event replay support.
27
- *
28
- * When enabled, published events are temporarily stored so new
29
- * subscribers can resume from a previous position using `lastEventId`.
30
- *
31
- * @default { enabled: false }
32
- */
33
- replay?: {
34
- /**
35
- * Whether event replay support is enabled.
36
- *
37
- * When enabled, published events are temporarily stored so new
38
- * subscribers can resume from a previous position using `lastEventId`.
39
- *
40
- * @default false
41
- */
42
- enabled: boolean;
43
- /**
44
- * How long (in seconds) to retain events for replay.
45
- *
46
- * Expired events are cleaned up lazily for performance reasons, so
47
- * some events may remain available slightly longer than this period.
48
- *
49
- * @default 300 (5 min)
50
- */
51
- seconds?: number;
52
- };
53
16
  }
54
- declare class RedisPublisher<T extends Record<string, object>> extends Publisher<T> {
17
+ /**
18
+ * Publisher adapter for Redis. Distributes events across processes via
19
+ * Redis Pub/Sub, with optional resume support backed by Redis Streams.
20
+ *
21
+ * @see {@link https://orpc.dev/docs/helpers/publisher#adapters | Publisher Helpers - Adapters}
22
+ */
23
+ declare class RedisPublisher<T extends Record<string, object>> extends BaseRedisPublisher<T> {
55
24
  private readonly redis;
56
25
  private readonly subscriber;
57
- private readonly prefix;
58
- private readonly serializer;
59
- private readonly replayEnabled;
60
- private readonly replaySeconds;
61
- /**
62
- * The exactness of the `XTRIM` command.
63
- * Used for testing purpose.
64
- */
65
- private readonly xTrimExactness;
66
- constructor(redis: RedisClientType<any, any, any, any, any>, options?: RedisPublisherOptions);
67
- private readonly firstPublishTimeMap;
68
- publish<K extends keyof T & string>(event: K, payload: T[K]): Promise<void>;
69
- protected subscribeListener<K extends keyof T & string>(event: K, originalListener: (payload: T[K]) => void, { lastEventId, onError }?: PublisherSubscribeListenerOptions): Promise<() => Promise<void>>;
70
- private serializePayload;
71
- private deserializePayload;
26
+ constructor(redis: RedisClientType<any, any, any, any, any>, { subscriber, ...options }?: RedisPublisherOptions);
27
+ protected publishMessage(channel: string, message: string): Promise<void>;
28
+ protected subscribeChannel(channel: string, listener: (message: unknown) => void): Promise<() => Promise<void>>;
29
+ protected addStreamEntry(key: string, data: string, trim?: RedisStreamTrimOptions): Promise<string>;
30
+ protected readStreamEntries(key: string, lastId: string): Promise<RedisStreamEntry[]>;
72
31
  }
73
32
 
74
33
  export { RedisPublisher };