@fedify/botkit-redis 0.5.0-dev.0 → 0.5.0-dev.329

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
@@ -84,6 +84,15 @@ The `RedisRepository` constructor accepts the following options:
84
84
  - **`clientOptions`** (optional): Additional node-redis client options.
85
85
  This option is only valid together with `url`.
86
86
 
87
+ - **`lockTimeoutMs`** (optional): How long a Redis lock can live without
88
+ renewal, in milliseconds. Defaults to `30000`.
89
+
90
+ - **`lockPollIntervalMs`** (optional): How long to wait before retrying a
91
+ held Redis lock, in milliseconds. Defaults to `20`.
92
+
93
+ - **`lockRenewIntervalMs`** (optional): How often to renew a held Redis lock,
94
+ in milliseconds. Defaults to `10000`.
95
+
87
96
  The options are mutually exclusive: use either `client` or `url`.
88
97
 
89
98
 
package/dist/mod.d.ts CHANGED
@@ -8,6 +8,7 @@ interface RedisClientLike {
8
8
  connect?(): Promise<unknown>;
9
9
  quit?(): Promise<unknown>;
10
10
  close?(): Promise<unknown> | unknown;
11
+ on?(event: "error", listener: (error: unknown) => void): unknown;
11
12
  sendCommand(args: readonly string[]): Promise<unknown>;
12
13
  }
13
14
  interface RedisRepositoryOptionsBase {
@@ -16,6 +17,21 @@ interface RedisRepositoryOptionsBase {
16
17
  * @default `"botkit"`
17
18
  */
18
19
  readonly prefix?: string;
20
+ /**
21
+ * How long a Redis lock can live without renewal, in milliseconds.
22
+ * @default `30000`
23
+ */
24
+ readonly lockTimeoutMs?: number;
25
+ /**
26
+ * How long to wait before retrying a held Redis lock, in milliseconds.
27
+ * @default `20`
28
+ */
29
+ readonly lockPollIntervalMs?: number;
30
+ /**
31
+ * How often to renew a held Redis lock, in milliseconds.
32
+ * @default `10000`
33
+ */
34
+ readonly lockRenewIntervalMs?: number;
19
35
  }
20
36
  interface RedisRepositoryOptionsWithClient extends RedisRepositoryOptionsBase {
21
37
  /**
@@ -57,7 +73,11 @@ type RedisRepositoryOptions = RedisRepositoryOptionsWithClient | RedisRepository
57
73
  declare class RedisRepository implements Repository, AsyncDisposable {
58
74
  private readonly client;
59
75
  private readonly ownsClient;
60
- private readonly ready;
76
+ private ready;
77
+ private readonly lockTimeoutMs;
78
+ private readonly lockPollIntervalMs;
79
+ private readonly lockRenewIntervalMs;
80
+ private closed;
61
81
  /**
62
82
  * The Redis key prefix under which all BotKit data is stored.
63
83
  */
@@ -65,6 +85,9 @@ declare class RedisRepository implements Repository, AsyncDisposable {
65
85
  /**
66
86
  * Creates a new Redis repository.
67
87
  * @param options The options for creating the repository.
88
+ * @throws {TypeError} If exactly one of `client` and `url` is not provided.
89
+ * @throws {RangeError} If a lock timing option is not a positive number, or
90
+ * if `lockRenewIntervalMs` is not less than `lockTimeoutMs`.
68
91
  */
69
92
  constructor(options: RedisRepositoryOptions);
70
93
  [Symbol.asyncDispose](): Promise<void>;
@@ -74,8 +97,11 @@ declare class RedisRepository implements Repository, AsyncDisposable {
74
97
  close(): Promise<void>;
75
98
  private key;
76
99
  private botKey;
100
+ private connect;
101
+ private ensureReady;
77
102
  private command;
78
103
  private get;
104
+ private mGet;
79
105
  private set;
80
106
  private del;
81
107
  private sAdd;
@@ -96,6 +122,11 @@ declare class RedisRepository implements Repository, AsyncDisposable {
96
122
  getMessages(identifier: string, options?: RepositoryGetMessagesOptions): AsyncIterable<Create | Announce>;
97
123
  getMessage(identifier: string, id: Uuid): Promise<Create | Announce | undefined>;
98
124
  countMessages(identifier: string): Promise<number>;
125
+ /**
126
+ * Adds a follower for a follow request.
127
+ *
128
+ * @throws {TypeError} If the follower actor has no ID.
129
+ */
99
130
  addFollower(identifier: string, followId: URL, follower: Actor): Promise<void>;
100
131
  removeFollower(identifier: string, followId: URL, followerId: URL): Promise<Actor | undefined>;
101
132
  private cleanupFollower;
@@ -109,10 +140,16 @@ declare class RedisRepository implements Repository, AsyncDisposable {
109
140
  removeFollowee(identifier: string, followeeId: URL): Promise<Follow | undefined>;
110
141
  getFollowee(identifier: string, followeeId: URL): Promise<Follow | undefined>;
111
142
  findFollowedBots(followeeId: URL): AsyncIterable<string>;
143
+ /**
144
+ * Stores a quote authorization.
145
+ *
146
+ * @throws {TypeError} If the quote authorization has no interacting object.
147
+ */
112
148
  addQuoteAuthorization(identifier: string, id: Uuid, authorization: QuoteAuthorization): Promise<void>;
113
149
  getQuoteAuthorization(identifier: string, id: Uuid): Promise<QuoteAuthorization | undefined>;
114
150
  findQuoteAuthorization(identifier: string, interactingObject: URL): Promise<QuoteAuthorization | undefined>;
115
151
  removeQuoteAuthorization(identifier: string, id: Uuid): Promise<QuoteAuthorization | undefined>;
152
+ private findStoredQuoteAuthorizationInteractingObject;
116
153
  addQuoteAuthorizationReference(identifier: string, authorization: URL, messageId: Uuid, attribution?: URL): Promise<void>;
117
154
  findQuoteAuthorizationReference(identifier: string, authorization: URL): Promise<Uuid | undefined>;
118
155
  findQuoteAuthorizationReferenceIdentifiers(authorization: URL): AsyncIterable<string>;
@@ -123,6 +160,7 @@ declare class RedisRepository implements Repository, AsyncDisposable {
123
160
  countVotes(identifier: string, messageId: Uuid): Promise<Readonly<Record<string, number>>>;
124
161
  forIdentifier(identifier: string): ActorScopedRepository;
125
162
  private quoteAuthorizationLockKey;
163
+ private quoteAuthorizationReferenceLockKey;
126
164
  }
127
165
  //#endregion
128
166
  export { RedisRepository, RedisRepositoryOptions };
package/dist/mod.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mod.d.ts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"mod.d.ts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;;UA4CU,eAAA;;EAAA,OAAA,GAAA,EAEI,OAFW,CAAA,OAAA,CAAA;EAAA,IAAA,GAAA,EAGd,OAHc,CAAA,OAAA,CAAA;EAAA,KAEX,GAAA,EAEF,OAFE,CAAA,OAAA,CAAA,GAAA,OAAA;EAAO,EAAA,EACV,KAAA,EAAA,OAAA,EAAA,QAAA,EAAA,CAAA,KAAA,EAAA,OAAA,EAAA,GAAA,IAAA,CAAA,EAAA,OAAA;EAAO,WACN,CAAA,IAAA,EAAA,SAAA,MAAA,EAAA,CAAA,EAE4B,OAF5B,CAAA,OAAA,CAAA;;AAEmC,UAarC,0BAAA,CAbqC;EAarC;AAA0B;;;EA8BF,SAJiB,MAAA,CAAA,EAAA,MAAA;EAA0B;AAAA;;;EAqBjD,SAUI,aAAA,CAAA,EAAA,MAAA;EAAkB;;AAdwB;AAqB1E;EAAkC,SAAA,kBAAA,CAAA,EAAA,MAAA;EAAA;;AAED;AAMjC;EAA6B,SAAA,mBAAA,CAAA,EAAA,MAAA;;UA9CnB,gCAAA,SAAyC,0BAsHlB,CAAA;EAAO;;;EA6Ob,SACtB,MAAA,EAhWc,eAgWd;EAAO;;;EAsBA,SACE,GAAA,CAAA,EAAA,KAAA;EAAM;;;EAoBR,SAEI,aAAA,CAAA,EAAA,KAAA;;UAhYN,6BAAA,SAAsC,0BAiYvC,CAAA;EAAM;;;EAAmD,SAAzB,GAAA,EAAA,MAAA,GA7XhB,GA6XgB;EAAO;;;EAoC7B,SAAG,MAAA,CAAA,EAAA,KAAA;EAAQ;;;EAyBL,SAAG,aAAA,CAAA,EAhbD,IAgbC,CAhbI,kBAgbJ,EAAA,KAAA,CAAA;;;;;;AA8CS,KAvdzB,sBAAA,GACR,gCAsdiC,GArdjC,6BAqdiC;;;;;AAyDrB,cAxgBH,eAAA,YAA2B,UAwgBxB,EAxgBoC,eAwgBpC,CAAA;EAAG,iBACN,MAAA;EAAK,iBAAb,UAAA;EAAO,QAsDwC,KAAA;EAAG,iBAAG,aAAA;EAAO,iBASpD,kBAAA;EAAkC,iBAC5B,mBAAA;EAAK,QAAnB,MAAA;EAAa;;;EAgCA,SACb,MAAA,EAAA,MAAA;EAAO;;;;;;;EAgCO,WACP,CAAA,OAAA,EAtnBW,sBAsnBX;EAAM,CAnkBT,MAAA,CAAO,YAAA,GAokBX,EApkB4B,OAokB5B,CAAA,IAAA,CAAA;EAAO;;;EAoBA,KAiBI,CAAA,CAAA,EAlmBC,OAkmBD,CAAA,IAAA,CAAA;EAAG,QACN,GAAA;EAAM,QAAd,MAAA;EAAO,QAY0B,OAAA;EAAG,QAAG,WAAA;EAAa,QAWjD,OAAA;EAAI,QACO,GAAA;EAAkB,QAChC,IAAA;EAAO,QA+CJ,GAAA;EAAI,QACC,GAAA;EAAkB,QAA1B,IAAA;EAAO,QAcW,IAAA;EAAG,QACb,KAAA;EAAkB,QAA1B,IAAA;EAAO,QA4BJ,IAAA;EAAI,QACC,MAAA;EAAkB,QAA1B,aAAA;EAAO,QAiEO,KAAA;EAAG,QACP,YAAA;EAAI,QACD,aAAA;EAAG,WAChB,CAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAtjBS,aAsjBT,EAAA,CAAA,EArjBA,OAqjBA,CAAA,IAAA,CAAA;EAAO,WAsBO,CAAA,UAAA,EAAA,MAAA,CAAA,EAjkBsB,OAikBtB,CAjkB8B,aAikB9B,EAAA,GAAA,SAAA,CAAA;EAAG,UACT,CAAA,UAAA,EAAA,MAAA,EAAA,EAAA,EAtjBL,IAsjBK,EAAA,QAAA,EArjBC,MAqjBD,GArjBU,QAqjBV,CAAA,EApjBR,OAojBQ,CAAA,IAAA,CAAA;EAAI,aAAZ,CAAA,UAAA,EAAA,MAAA,EAAA,EAAA,EAjiBG,IAiiBH,EAAA,OAAA,EAAA,CAAA,QAAA,EA/hBW,MA+hBX,GA/hBoB,QA+hBpB,EAAA,GA9hBI,MA8hBJ,GA9hBa,QA8hBb,GAAA,SAAA,GA9hBoC,OA8hBpC,CA9hB4C,MA8hB5C,GA9hBqD,QA8hBrD,GAAA,SAAA,CAAA,CAAA,EA7hBA,OA6hBA,CAAA,OAAA,CAAA;EAAO,aAeO,CAAA,UAAA,EAAA,MAAA,EAAA,EAAA,EA1gBX,IA0gBW,CAAA,EAzgBd,OAygBc,CAzgBN,MAygBM,GAzgBG,QAygBH,GAAA,SAAA,CAAA;EAAG,WACjB,CAAA,UAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EAlfQ,4BAkfR,CAAA,EAjfA,aAifA,CAjfc,MAifd,GAjfuB,QAifvB,CAAA;EAAa,UAQC,CAAA,UAAA,EAAA,MAAA,EAAA,EAAA,EA1dX,IA0dW,CAAA,EAzdd,OAydc,CAzdN,MAydM,GAzdG,QAydH,GAAA,SAAA,CAAA;EAAG,aACT,CAAA,UAAA,EAAA,MAAA,CAAA,EA5cwB,OA4cxB,CAAA,MAAA,CAAA;EAAG;;;;;EAyCA,WAEX,CAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EA5eS,GA4eT,EAAA,QAAA,EA3eS,KA2eT,CAAA,EA1eA,OA0eA,CAAA,IAAA,CAAA;EAAO,cAgBiC,CAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EA/c/B,GA+c+B,EAAA,UAAA,EA9c7B,GA8c6B,CAAA,EA7cxC,OA6cwC,CA7chC,KA6cgC,GAAA,SAAA,CAAA;EAAI,QAAG,eAAA;EAAO,WAM5C,CAAA,UAAA,EAAA,MAAA,EAAA,UAAA,EA7ZqC,GA6ZrC,CAAA,EA7Z2C,OA6Z3C,CAAA,OAAA,CAAA;EAAI,YACG,CAAA,UAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EArZT,6BAqZS,CAAA,EApZjB,aAoZiB,CApZH,KAoZG,CAAA;EAAM,cAAf,CAAA,UAAA,EAAA,MAAA,CAAA,EA3XyB,OA2XzB,CAAA,MAAA,CAAA;EAAQ,aAAhB,CAAA,UAAA,EAAA,MAAA,EAAA,EAAA,EArXG,IAqXH,EAAA,MAAA,EApXO,MAoXP,CAAA,EAnXA,OAmXA,CAAA,IAAA,CAAA;EAAO,gBAayB,CAAA,UAAA,EAAA,MAAA,EAAA,EAAA,EAvX7B,IAuX6B,CAAA,EAtXhC,OAsXgC,CAtXxB,MAsXwB,GAAA,SAAA,CAAA;EAAqB,aA1+BlB,CAAA,UAAA,EAAA,MAAA,EAAA,EAAA,EA6nBhC,IA7nBgC,CAAA,EA8nBnC,OA9nBmC,CA8nB3B,MA9nB2B,GAAA,SAAA,CAAA;EAAU,WAAE,CAAA,UAAA,EAAA,MAAA,EAAA,UAAA,EA0oBpC,GA1oBoC,EAAA,MAAA,EA2oBxC,MA3oBwC,CAAA,EA4oB/C,OA5oB+C,CAAA,IAAA,CAAA;EAAe,cAAA,CAAA,UAAA,EAAA,MAAA,EAAA,UAAA,EA+pBnD,GA/pBmD,CAAA,EAgqB9D,OAhqB8D,CAgqBtD,MAhqBsD,GAAA,SAAA,CAAA;8CAirBnD,MACX,QAAQ;+BAYyB,MAAM;;;;;;gDAWpC,qBACW,qBACd;gDA+CG,OACH,QAAQ;gEAcU,MAClB,QAAQ;mDA4BL,OACH,QAAQ;;oEAiEM,gBACJ,oBACG,MACb;qEAsBc,MACd,QAAQ;4DAeM,MACd;gFAQc,MACd,QAAQ;uEAsBM,MACd;sCAiBU,eACF,sBAER;6CAgBwC,OAAO;4CAMrC,OACV,QAAQ,SAAS;qCAae"}
package/dist/mod.js CHANGED
@@ -6,9 +6,10 @@ import { createClient } from "redis";
6
6
 
7
7
  //#region src/mod.ts
8
8
  const logger = getLogger(["botkit", "redis"]);
9
- const redisLockTimeoutMs = 3e4;
10
- const redisLockPollIntervalMs = 20;
11
- const redisLockRenewIntervalMs = 1e3;
9
+ const defaultRedisLockTimeoutMs = 3e4;
10
+ const defaultRedisLockPollIntervalMs = 20;
11
+ const defaultRedisLockRenewIntervalMs = 1e4;
12
+ const redisMGetBatchSize = 100;
12
13
  const uuidV7Pattern = /^([0-9a-f]{8})-([0-9a-f]{4})-7[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
13
14
  /**
14
15
  * A repository for storing bot data using Redis.
@@ -18,6 +19,10 @@ var RedisRepository = class {
18
19
  client;
19
20
  ownsClient;
20
21
  ready;
22
+ lockTimeoutMs;
23
+ lockPollIntervalMs;
24
+ lockRenewIntervalMs;
25
+ closed = false;
21
26
  /**
22
27
  * The Redis key prefix under which all BotKit data is stored.
23
28
  */
@@ -25,16 +30,26 @@ var RedisRepository = class {
25
30
  /**
26
31
  * Creates a new Redis repository.
27
32
  * @param options The options for creating the repository.
33
+ * @throws {TypeError} If exactly one of `client` and `url` is not provided.
34
+ * @throws {RangeError} If a lock timing option is not a positive number, or
35
+ * if `lockRenewIntervalMs` is not less than `lockTimeoutMs`.
28
36
  */
29
37
  constructor(options) {
38
+ if (options == null) throw new TypeError("RedisRepositoryOptions must be provided.");
30
39
  const hasClient = "client" in options && options.client != null;
31
40
  const hasUrl = "url" in options && options.url != null;
32
41
  if (hasClient === hasUrl) throw new TypeError("RedisRepositoryOptions must provide exactly one of client or url.");
33
42
  this.prefix = options.prefix ?? "botkit";
43
+ this.lockTimeoutMs = options.lockTimeoutMs ?? defaultRedisLockTimeoutMs;
44
+ this.lockPollIntervalMs = options.lockPollIntervalMs ?? defaultRedisLockPollIntervalMs;
45
+ this.lockRenewIntervalMs = options.lockRenewIntervalMs ?? defaultRedisLockRenewIntervalMs;
46
+ validatePositiveMilliseconds("lockTimeoutMs", this.lockTimeoutMs);
47
+ validatePositiveMilliseconds("lockPollIntervalMs", this.lockPollIntervalMs);
48
+ validatePositiveMilliseconds("lockRenewIntervalMs", this.lockRenewIntervalMs);
49
+ if (this.lockRenewIntervalMs >= this.lockTimeoutMs) throw new RangeError("lockRenewIntervalMs must be less than lockTimeoutMs.");
34
50
  if (hasClient) {
35
51
  this.client = options.client;
36
52
  this.ownsClient = false;
37
- this.ready = Promise.resolve();
38
53
  } else {
39
54
  const client = createClient({
40
55
  ...options.clientOptions,
@@ -42,7 +57,13 @@ var RedisRepository = class {
42
57
  });
43
58
  this.client = client;
44
59
  this.ownsClient = true;
45
- this.ready = client.connect?.() ?? Promise.resolve();
60
+ client.on?.("error", (error) => {
61
+ logger.warn("Owned Redis client emitted an error: {error}", { error });
62
+ });
63
+ const ready = this.connect();
64
+ ready.catch(() => {
65
+ if (this.ready === ready) this.ready = void 0;
66
+ });
46
67
  }
47
68
  }
48
69
  async [Symbol.asyncDispose]() {
@@ -52,10 +73,22 @@ var RedisRepository = class {
52
73
  * Closes the owned Redis connection.
53
74
  */
54
75
  async close() {
55
- await this.ready;
76
+ if (this.closed) return;
77
+ this.closed = true;
56
78
  if (!this.ownsClient) return;
57
- if (this.client.quit != null) await this.client.quit();
58
- else await this.client.close?.();
79
+ const ready = this.ready;
80
+ try {
81
+ await ready;
82
+ } catch (error) {
83
+ logger.warn("Owned Redis client did not become ready before close: {error}", { error });
84
+ if (this.ready === ready) this.ready = void 0;
85
+ }
86
+ try {
87
+ if (this.client.quit != null) await this.client.quit();
88
+ else await this.client.close?.();
89
+ } catch (error) {
90
+ logger.warn("Failed to close owned Redis client: {error}", { error });
91
+ }
59
92
  }
60
93
  key(...segments) {
61
94
  return [this.prefix, ...segments.map((segment) => encodeURIComponent(segment))].join(":");
@@ -63,8 +96,35 @@ var RedisRepository = class {
63
96
  botKey(identifier, ...segments) {
64
97
  return this.key("bots", identifier, ...segments);
65
98
  }
99
+ connect() {
100
+ const ready = this.client.connect?.() ?? Promise.resolve();
101
+ this.ready = ready;
102
+ return ready;
103
+ }
104
+ async ensureReady() {
105
+ if (this.closed) throw new TypeError("RedisRepository is closed.");
106
+ if (!this.ownsClient) return;
107
+ const ready = this.ready;
108
+ if (ready == null) {
109
+ if (this.client.isOpen) return;
110
+ const nextReady = this.connect();
111
+ try {
112
+ await nextReady;
113
+ } catch (error) {
114
+ if (this.ready === nextReady) this.ready = void 0;
115
+ throw error;
116
+ }
117
+ return;
118
+ }
119
+ try {
120
+ await ready;
121
+ } catch (error) {
122
+ if (this.ready === ready) this.ready = void 0;
123
+ throw error;
124
+ }
125
+ }
66
126
  async command(args) {
67
- await this.ready;
127
+ await this.ensureReady();
68
128
  try {
69
129
  return await this.client.sendCommand(args);
70
130
  } catch (error) {
@@ -79,6 +139,13 @@ var RedisRepository = class {
79
139
  const value = await this.command(["GET", key]);
80
140
  return typeof value === "string" ? value : void 0;
81
141
  }
142
+ async mGet(keys) {
143
+ if (keys.length < 1) return [];
144
+ const chunks = [];
145
+ for (let i = 0; i < keys.length; i += redisMGetBatchSize) chunks.push(keys.slice(i, i + redisMGetBatchSize));
146
+ const replies = await Promise.all(chunks.map((chunk) => this.command(["MGET", ...chunk])));
147
+ return replies.flatMap(toStringArray);
148
+ }
82
149
  async set(key, value) {
83
150
  await this.command([
84
151
  "SET",
@@ -156,24 +223,30 @@ var RedisRepository = class {
156
223
  token,
157
224
  "NX",
158
225
  "PX",
159
- redisLockTimeoutMs.toString()
160
- ]) !== "OK") await delay(redisLockPollIntervalMs);
226
+ this.lockTimeoutMs.toString()
227
+ ]) !== "OK") await delay(this.lockPollIntervalMs);
161
228
  const renew = setInterval(() => {
162
229
  (async () => {
163
230
  try {
164
- await this.command([
231
+ if (this.closed) {
232
+ clearInterval(renew);
233
+ return;
234
+ }
235
+ const renewed = toNumber(await this.command([
165
236
  "EVAL",
166
237
  "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('PEXPIRE', KEYS[1], ARGV[2]) else return 0 end",
167
238
  "1",
168
239
  lockKey,
169
240
  token,
170
- redisLockTimeoutMs.toString()
171
- ]);
241
+ this.lockTimeoutMs.toString()
242
+ ]));
243
+ if (renewed === 0) clearInterval(renew);
172
244
  } catch (error) {
173
- logger.warn("Failed to renew Redis lock: {error}", { error });
245
+ if (this.closed) clearInterval(renew);
246
+ else logger.warn("Failed to renew Redis lock: {error}", { error });
174
247
  }
175
248
  })();
176
- }, redisLockRenewIntervalMs);
249
+ }, this.lockRenewIntervalMs);
177
250
  try {
178
251
  return await operation();
179
252
  } finally {
@@ -192,11 +265,10 @@ var RedisRepository = class {
192
265
  }
193
266
  }
194
267
  async setKeyPairs(identifier, keyPairs) {
195
- const pairs = [];
196
- for (const keyPair of keyPairs) pairs.push({
268
+ const pairs = await Promise.all(keyPairs.map(async (keyPair) => ({
197
269
  private: await exportJwk(keyPair.privateKey),
198
270
  public: await exportJwk(keyPair.publicKey)
199
- });
271
+ })));
200
272
  await this.set(this.botKey(identifier, "keyPairs"), JSON.stringify(pairs));
201
273
  }
202
274
  async getKeyPairs(identifier) {
@@ -209,15 +281,22 @@ var RedisRepository = class {
209
281
  })));
210
282
  }
211
283
  async addMessage(identifier, id, activity) {
212
- await this.set(this.botKey(identifier, "messages", id), JSON.stringify(await activity.toJsonLd({ format: "compact" })));
213
- await this.zAdd(this.botKey(identifier, "messages"), getMessageScore(id, activity), id);
284
+ await this.withRedisLock(this.botKey(identifier, "messages", id, "lock"), async () => {
285
+ await this.set(this.botKey(identifier, "messages", id), JSON.stringify(await activity.toJsonLd({ format: "compact" })));
286
+ await this.zAdd(this.botKey(identifier, "messages"), getMessageScore(id, activity), id);
287
+ });
214
288
  }
215
289
  async updateMessage(identifier, id, updater) {
216
290
  const key = this.botKey(identifier, "messages", id);
217
291
  return await this.withRedisLock(this.botKey(identifier, "messages", id, "lock"), async () => {
218
292
  const json = await this.get(key);
219
293
  if (json == null) return false;
220
- const activity = await Activity.fromJsonLd(JSON.parse(json));
294
+ let activity;
295
+ try {
296
+ activity = await Activity.fromJsonLd(JSON.parse(json));
297
+ } catch {
298
+ return false;
299
+ }
221
300
  if (!(activity instanceof Create || activity instanceof Announce)) return false;
222
301
  const updated = await updater(activity);
223
302
  if (updated == null) return false;
@@ -228,27 +307,32 @@ var RedisRepository = class {
228
307
  }
229
308
  async removeMessage(identifier, id) {
230
309
  const key = this.botKey(identifier, "messages", id);
231
- const json = await this.get(key);
232
- await this.del(key);
233
- await this.zRem(this.botKey(identifier, "messages"), id);
234
- if (json == null) return void 0;
235
- const activity = await Activity.fromJsonLd(JSON.parse(json));
236
- if (activity instanceof Create || activity instanceof Announce) return activity;
237
- return void 0;
310
+ return await this.withRedisLock(this.botKey(identifier, "messages", id, "lock"), async () => {
311
+ const json = await this.get(key);
312
+ await this.del(key);
313
+ await this.zRem(this.botKey(identifier, "messages"), id);
314
+ if (json == null) return void 0;
315
+ try {
316
+ const activity = await Activity.fromJsonLd(JSON.parse(json));
317
+ if (activity instanceof Create || activity instanceof Announce) return activity;
318
+ } catch {
319
+ return void 0;
320
+ }
321
+ return void 0;
322
+ });
238
323
  }
239
324
  async *getMessages(identifier, options = {}) {
240
325
  const min = options.since == null ? "-inf" : options.since.epochMilliseconds.toString();
241
326
  const max = options.until == null ? "+inf" : options.until.epochMilliseconds.toString();
242
327
  const ids = await this.zRangeByScore(this.botKey(identifier, "messages"), min, max, options.order == null || options.order === "newest", options.limit);
243
- for (const id of ids) {
244
- const json = await this.get(this.botKey(identifier, "messages", id));
245
- if (json == null) continue;
246
- try {
247
- const activity = await Activity.fromJsonLd(JSON.parse(json));
248
- if (activity instanceof Create || activity instanceof Announce) yield activity;
249
- } catch {
250
- continue;
251
- }
328
+ if (ids.length < 1) return;
329
+ const keys = ids.map((id) => this.botKey(identifier, "messages", id));
330
+ const jsons = await this.mGet(keys);
331
+ for (const json of jsons) try {
332
+ const activity = await Activity.fromJsonLd(JSON.parse(json));
333
+ if (activity instanceof Create || activity instanceof Announce) yield activity;
334
+ } catch {
335
+ continue;
252
336
  }
253
337
  }
254
338
  async getMessage(identifier, id) {
@@ -265,6 +349,11 @@ var RedisRepository = class {
265
349
  countMessages(identifier) {
266
350
  return this.zCard(this.botKey(identifier, "messages"));
267
351
  }
352
+ /**
353
+ * Adds a follower for a follow request.
354
+ *
355
+ * @throws {TypeError} If the follower actor has no ID.
356
+ */
268
357
  async addFollower(identifier, followId, follower) {
269
358
  if (follower.id == null) throw new TypeError("The follower ID is missing.");
270
359
  const followerId = follower.id.href;
@@ -287,10 +376,10 @@ var RedisRepository = class {
287
376
  const requestKey = this.botKey(identifier, "followRequests", followIdString);
288
377
  return await this.withRedisLock(this.botKey(identifier, "followers", "lock"), async () => {
289
378
  const currentFollowerId = await this.get(requestKey);
290
- if (currentFollowerId !== followerId.href) return void 0;
379
+ if (currentFollowerId != null && currentFollowerId !== followerId.href) return void 0;
291
380
  const followerKey = this.botKey(identifier, "followers", followerId.href);
292
381
  const json = await this.get(followerKey);
293
- await this.del(requestKey);
382
+ if (currentFollowerId === followerId.href) await this.del(requestKey);
294
383
  await this.sRem(this.botKey(identifier, "followerRequests", followerId.href), followIdString);
295
384
  const removed = await this.cleanupFollower(identifier, followerId.href);
296
385
  if (!removed || json == null) return void 0;
@@ -314,6 +403,7 @@ var RedisRepository = class {
314
403
  return await this.get(this.botKey(identifier, "followers", followerId.href)) != null;
315
404
  }
316
405
  async *getFollowers(identifier, options = {}) {
406
+ if (options.limit === 0) return;
317
407
  const start = options.offset ?? 0;
318
408
  const stop = options.limit == null ? -1 : start + options.limit - 1;
319
409
  const ids = toStringArray(await this.command([
@@ -322,15 +412,14 @@ var RedisRepository = class {
322
412
  start.toString(),
323
413
  stop.toString()
324
414
  ]));
325
- for (const id of ids) {
326
- const json = await this.get(this.botKey(identifier, "followers", id));
327
- if (json == null) continue;
328
- try {
329
- const actor = await Object$1.fromJsonLd(JSON.parse(json));
330
- if (isActor(actor)) yield actor;
331
- } catch {
332
- continue;
333
- }
415
+ if (ids.length < 1) return;
416
+ const keys = ids.map((id) => this.botKey(identifier, "followers", id));
417
+ const jsons = await this.mGet(keys);
418
+ for (const json of jsons) try {
419
+ const actor = await Object$1.fromJsonLd(JSON.parse(json));
420
+ if (isActor(actor)) yield actor;
421
+ } catch {
422
+ continue;
334
423
  }
335
424
  }
336
425
  countFollowers(identifier) {
@@ -355,14 +444,18 @@ var RedisRepository = class {
355
444
  }
356
445
  }
357
446
  async addFollowee(identifier, followeeId, follow) {
358
- await this.set(this.botKey(identifier, "followees", followeeId.href), JSON.stringify(await follow.toJsonLd({ format: "compact" })));
359
- await this.zAdd(this.key("index", "followees", followeeId.href), 0, identifier);
447
+ await this.withRedisLock(this.botKey(identifier, "followees", followeeId.href, "lock"), async () => {
448
+ await this.set(this.botKey(identifier, "followees", followeeId.href), JSON.stringify(await follow.toJsonLd({ format: "compact" })));
449
+ await this.zAdd(this.key("index", "followees", followeeId.href), 0, identifier);
450
+ });
360
451
  }
361
452
  async removeFollowee(identifier, followeeId) {
362
- const follow = await this.getFollowee(identifier, followeeId);
363
- await this.del(this.botKey(identifier, "followees", followeeId.href));
364
- await this.zRem(this.key("index", "followees", followeeId.href), identifier);
365
- return follow;
453
+ return await this.withRedisLock(this.botKey(identifier, "followees", followeeId.href, "lock"), async () => {
454
+ const follow = await this.getFollowee(identifier, followeeId);
455
+ await this.del(this.botKey(identifier, "followees", followeeId.href));
456
+ await this.zRem(this.key("index", "followees", followeeId.href), identifier);
457
+ return follow;
458
+ });
366
459
  }
367
460
  async getFollowee(identifier, followeeId) {
368
461
  const json = await this.get(this.botKey(identifier, "followees", followeeId.href));
@@ -376,6 +469,11 @@ var RedisRepository = class {
376
469
  async *findFollowedBots(followeeId) {
377
470
  yield* await this.zRange(this.key("index", "followees", followeeId.href));
378
471
  }
472
+ /**
473
+ * Stores a quote authorization.
474
+ *
475
+ * @throws {TypeError} If the quote authorization has no interacting object.
476
+ */
379
477
  async addQuoteAuthorization(identifier, id, authorization) {
380
478
  const interactingObject = authorization.interactingObjectId;
381
479
  if (interactingObject == null) throw new TypeError("The quote authorization interacting object is missing.");
@@ -384,9 +482,10 @@ var RedisRepository = class {
384
482
  const existing = await this.get(indexKey);
385
483
  if (existing != null) {
386
484
  if (await this.getQuoteAuthorization(identifier, existing) != null) return;
387
- await this.del(indexKey);
485
+ await this.del(indexKey, this.botKey(identifier, "quoteAuthorizationInteractingObjects", existing));
388
486
  }
389
487
  await this.set(this.botKey(identifier, "quoteAuthorizations", id), JSON.stringify(await authorization.toJsonLd({ format: "compact" })));
488
+ await this.set(this.botKey(identifier, "quoteAuthorizationInteractingObjects", id), interactingObject.href);
390
489
  await this.set(indexKey, id);
391
490
  });
392
491
  }
@@ -405,47 +504,67 @@ var RedisRepository = class {
405
504
  const id = await this.get(indexKey);
406
505
  if (id == null) return void 0;
407
506
  const authorization = await this.getQuoteAuthorization(identifier, id);
408
- if (authorization == null && await this.get(indexKey) === id) await this.del(indexKey);
507
+ if (authorization == null && await this.get(indexKey) === id) await this.del(indexKey, this.botKey(identifier, "quoteAuthorizationInteractingObjects", id));
409
508
  return authorization;
410
509
  });
411
510
  }
412
511
  async removeQuoteAuthorization(identifier, id) {
413
512
  const key = this.botKey(identifier, "quoteAuthorizations", id);
414
- const json = await this.get(key);
415
- await this.del(key);
416
- if (json == null) return void 0;
417
- let parsed;
513
+ const interactingObjectKey = this.botKey(identifier, "quoteAuthorizationInteractingObjects", id);
514
+ const storedInteractingObject = await this.get(interactingObjectKey) ?? await this.findStoredQuoteAuthorizationInteractingObject(key);
515
+ if (storedInteractingObject == null) return void 0;
516
+ const indexKey = this.botKey(identifier, "quoteAuthorizationsByInteractingObject", storedInteractingObject);
517
+ const parsed = await this.withRedisLock(this.quoteAuthorizationLockKey(indexKey), async () => {
518
+ const json = await this.get(key);
519
+ if (json == null) {
520
+ await this.del(interactingObjectKey);
521
+ return void 0;
522
+ }
523
+ let parsed$1;
524
+ try {
525
+ parsed$1 = JSON.parse(json);
526
+ } catch {
527
+ return void 0;
528
+ }
529
+ const interactingObject = getRawQuoteAuthorizationInteractingObject(parsed$1);
530
+ if (interactingObject?.href !== storedInteractingObject) return void 0;
531
+ await this.del(key, interactingObjectKey);
532
+ if (await this.get(indexKey) === id) await this.del(indexKey);
533
+ return parsed$1;
534
+ });
535
+ if (parsed == null) return void 0;
418
536
  try {
419
- parsed = JSON.parse(json);
537
+ return await QuoteAuthorization.fromJsonLd(parsed);
420
538
  } catch {
421
539
  return void 0;
422
540
  }
423
- const interactingObject = getRawQuoteAuthorizationInteractingObject(parsed);
424
- if (interactingObject != null) {
425
- const indexKey = this.botKey(identifier, "quoteAuthorizationsByInteractingObject", interactingObject.href);
426
- await this.withRedisLock(this.quoteAuthorizationLockKey(indexKey), async () => {
427
- if (await this.get(indexKey) === id) await this.del(indexKey);
428
- });
429
- }
541
+ }
542
+ async findStoredQuoteAuthorizationInteractingObject(key) {
543
+ const json = await this.get(key);
544
+ if (json == null) return void 0;
430
545
  try {
431
- return await QuoteAuthorization.fromJsonLd(parsed);
546
+ const interactingObject = getRawQuoteAuthorizationInteractingObject(JSON.parse(json));
547
+ return interactingObject?.href;
432
548
  } catch {
433
549
  return void 0;
434
550
  }
435
551
  }
436
552
  async addQuoteAuthorizationReference(identifier, authorization, messageId, attribution) {
437
- const value = attribution == null ? { messageId } : {
438
- messageId,
439
- attribution: attribution.href
440
- };
441
- await this.set(this.botKey(identifier, "quoteAuthorizationRefs", authorization.href), JSON.stringify(value));
442
- await this.zAdd(this.key("index", "quoteAuthorizationRefs", authorization.href), 0, identifier);
553
+ await this.withRedisLock(this.quoteAuthorizationReferenceLockKey(identifier, authorization), async () => {
554
+ const value = attribution == null ? { messageId } : {
555
+ messageId,
556
+ attribution: attribution.href
557
+ };
558
+ await this.set(this.botKey(identifier, "quoteAuthorizationRefs", authorization.href), JSON.stringify(value));
559
+ await this.zAdd(this.key("index", "quoteAuthorizationRefs", authorization.href), 0, identifier);
560
+ });
443
561
  }
444
562
  async findQuoteAuthorizationReference(identifier, authorization) {
445
563
  const json = await this.get(this.botKey(identifier, "quoteAuthorizationRefs", authorization.href));
446
564
  if (json == null) return void 0;
447
565
  try {
448
566
  const value = JSON.parse(json);
567
+ if (typeof value !== "object" || value == null) return void 0;
449
568
  return value.messageId;
450
569
  } catch {
451
570
  return void 0;
@@ -463,6 +582,7 @@ var RedisRepository = class {
463
582
  } catch {
464
583
  return void 0;
465
584
  }
585
+ if (typeof value !== "object" || value == null) return void 0;
466
586
  if (value.attribution == null) return void 0;
467
587
  try {
468
588
  return new URL(value.attribution);
@@ -471,8 +591,10 @@ var RedisRepository = class {
471
591
  }
472
592
  }
473
593
  async removeQuoteAuthorizationReference(identifier, authorization) {
474
- await this.del(this.botKey(identifier, "quoteAuthorizationRefs", authorization.href));
475
- await this.zRem(this.key("index", "quoteAuthorizationRefs", authorization.href), identifier);
594
+ await this.withRedisLock(this.quoteAuthorizationReferenceLockKey(identifier, authorization), async () => {
595
+ await this.del(this.botKey(identifier, "quoteAuthorizationRefs", authorization.href));
596
+ await this.zRem(this.key("index", "quoteAuthorizationRefs", authorization.href), identifier);
597
+ });
476
598
  }
477
599
  async vote(identifier, messageId, voterId, option) {
478
600
  await this.sAdd(this.botKey(identifier, "polls", messageId, "voters"), voterId.href);
@@ -485,7 +607,9 @@ var RedisRepository = class {
485
607
  async countVotes(identifier, messageId) {
486
608
  const result = {};
487
609
  const options = await this.zRange(this.botKey(identifier, "polls", messageId, "options"));
488
- for (const option of options) result[option] = await this.sCard(this.botKey(identifier, "polls", messageId, "options", option));
610
+ await Promise.all(options.map(async (option) => {
611
+ result[option] = await this.sCard(this.botKey(identifier, "polls", messageId, "options", option));
612
+ }));
489
613
  return result;
490
614
  }
491
615
  forIdentifier(identifier) {
@@ -494,6 +618,9 @@ var RedisRepository = class {
494
618
  quoteAuthorizationLockKey(indexKey) {
495
619
  return `${indexKey}:lock`;
496
620
  }
621
+ quoteAuthorizationReferenceLockKey(identifier, authorization) {
622
+ return this.botKey(identifier, "quoteAuthorizationRefs", authorization.href, "lock");
623
+ }
497
624
  };
498
625
  function toNumber(value) {
499
626
  if (typeof value === "number") return value;
@@ -501,6 +628,9 @@ function toNumber(value) {
501
628
  if (typeof value === "string") return Number.parseInt(value, 10);
502
629
  return 0;
503
630
  }
631
+ function validatePositiveMilliseconds(name, value) {
632
+ if (!Number.isFinite(value) || value <= 0) throw new RangeError(`${name} must be a positive number.`);
633
+ }
504
634
  function toStringArray(value) {
505
635
  if (!Array.isArray(value)) return [];
506
636
  return value.filter((item) => typeof item === "string");
package/dist/mod.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mod.js","names":["options: RedisRepositoryOptions","identifier: string","args: readonly string[]","key: string","value: string","member: string","score: number","min: string","max: string","reverse: boolean","limit?: number","lockKey: string","operation: () => Promise<T>","keyPairs: CryptoKeyPair[]","pairs: KeyPair[]","id: Uuid","activity: Create | Announce","updater: (\n existing: Create | Announce,\n ) => Create | Announce | undefined | Promise<Create | Announce | undefined>","options: RepositoryGetMessagesOptions","followId: URL","follower: Actor","followerId: URL","followerId: string","options: RepositoryGetFollowersOptions","follow: Follow","followeeId: URL","authorization: QuoteAuthorization","interactingObject: URL","parsed: unknown","authorization: URL","messageId: Uuid","attribution?: URL","value: QuoteAuthorizationReferenceData","voterId: URL","option: string","result: Record<string, number>","indexKey: string","value: unknown","ms: number","uuid: string","json: unknown"],"sources":["../src/mod.ts"],"sourcesContent":["// BotKit by Fedify: A framework for creating ActivityPub bots\n// Copyright (C) 2026 Hong Minhee <https://hongminhee.org/>\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\nimport {\n ActorScopedRepository,\n type Repository,\n type RepositoryGetFollowersOptions,\n type RepositoryGetMessagesOptions,\n type Uuid,\n} from \"@fedify/botkit/repository\";\nimport { exportJwk, importJwk } from \"@fedify/fedify/sig\";\nimport {\n Activity,\n type Actor,\n Announce,\n Create,\n Follow,\n isActor,\n Object,\n QuoteAuthorization,\n} from \"@fedify/vocab\";\nimport { getLogger } from \"@logtape/logtape\";\nimport { createClient, type RedisClientOptions } from \"redis\";\n\nconst logger = getLogger([\"botkit\", \"redis\"]);\nconst redisLockTimeoutMs = 30_000;\nconst redisLockPollIntervalMs = 20;\nconst redisLockRenewIntervalMs = 1_000;\nconst uuidV7Pattern =\n /^([0-9a-f]{8})-([0-9a-f]{4})-7[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\ninterface RedisClientLike {\n readonly isOpen?: boolean;\n connect?(): Promise<unknown>;\n quit?(): Promise<unknown>;\n close?(): Promise<unknown> | unknown;\n sendCommand(args: readonly string[]): Promise<unknown>;\n}\n\ninterface KeyPair {\n readonly private: JsonWebKey;\n readonly public: JsonWebKey;\n}\n\ninterface QuoteAuthorizationReferenceData {\n readonly messageId: Uuid;\n readonly attribution?: string;\n}\n\ninterface RedisRepositoryOptionsBase {\n /**\n * The Redis key prefix under which all BotKit data is stored.\n * @default `\"botkit\"`\n */\n readonly prefix?: string;\n}\n\ninterface RedisRepositoryOptionsWithClient extends RedisRepositoryOptionsBase {\n /**\n * A pre-configured Redis client to use. It must already be connected.\n */\n readonly client: RedisClientLike;\n\n /**\n * Disallowed when `client` is provided.\n */\n readonly url?: never;\n\n /**\n * Disallowed when `client` is provided.\n */\n readonly clientOptions?: never;\n}\n\ninterface RedisRepositoryOptionsWithUrl extends RedisRepositoryOptionsBase {\n /**\n * A Redis connection string to connect with.\n */\n readonly url: string | URL;\n\n /**\n * Disallowed when `url` is provided.\n */\n readonly client?: never;\n\n /**\n * Additional node-redis client options.\n */\n readonly clientOptions?: Omit<RedisClientOptions, \"url\">;\n}\n\n/**\n * Options for creating a Redis repository.\n * @since 0.5.0\n */\nexport type RedisRepositoryOptions =\n | RedisRepositoryOptionsWithClient\n | RedisRepositoryOptionsWithUrl;\n\n/**\n * A repository for storing bot data using Redis.\n * @since 0.5.0\n */\nexport class RedisRepository implements Repository, AsyncDisposable {\n private readonly client: RedisClientLike;\n private readonly ownsClient: boolean;\n private readonly ready: Promise<unknown>;\n\n /**\n * The Redis key prefix under which all BotKit data is stored.\n */\n readonly prefix: string;\n\n /**\n * Creates a new Redis repository.\n * @param options The options for creating the repository.\n */\n constructor(options: RedisRepositoryOptions) {\n const hasClient = \"client\" in options && options.client != null;\n const hasUrl = \"url\" in options && options.url != null;\n if (hasClient === hasUrl) {\n throw new TypeError(\n \"RedisRepositoryOptions must provide exactly one of client or url.\",\n );\n }\n this.prefix = options.prefix ?? \"botkit\";\n if (hasClient) {\n this.client = options.client;\n this.ownsClient = false;\n this.ready = Promise.resolve();\n } else {\n const client = createClient({\n ...options.clientOptions,\n url: options.url.toString(),\n }) as unknown as RedisClientLike;\n this.client = client;\n this.ownsClient = true;\n this.ready = client.connect?.() ?? Promise.resolve();\n }\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.close();\n }\n\n /**\n * Closes the owned Redis connection.\n */\n async close(): Promise<void> {\n await this.ready;\n if (!this.ownsClient) return;\n if (this.client.quit != null) {\n await this.client.quit();\n } else {\n await this.client.close?.();\n }\n }\n\n private key(...segments: readonly string[]): string {\n return [\n this.prefix,\n ...segments.map((segment) => encodeURIComponent(segment)),\n ].join(\":\");\n }\n\n private botKey(identifier: string, ...segments: readonly string[]): string {\n return this.key(\"bots\", identifier, ...segments);\n }\n\n private async command(args: readonly string[]): Promise<unknown> {\n await this.ready;\n try {\n return await this.client.sendCommand(args);\n } catch (error) {\n logger.error(\"Redis command failed: {command}.\", {\n command: args[0],\n error,\n });\n throw error;\n }\n }\n\n private async get(key: string): Promise<string | undefined> {\n const value = await this.command([\"GET\", key]);\n return typeof value === \"string\" ? value : undefined;\n }\n\n private async set(key: string, value: string): Promise<void> {\n await this.command([\"SET\", key, value]);\n }\n\n private async del(...keys: readonly string[]): Promise<void> {\n if (keys.length < 1) return;\n await this.command([\"DEL\", ...keys]);\n }\n\n private async sAdd(key: string, member: string): Promise<void> {\n await this.command([\"SADD\", key, member]);\n }\n\n private async sRem(key: string, member: string): Promise<void> {\n await this.command([\"SREM\", key, member]);\n }\n\n private async sCard(key: string): Promise<number> {\n return toNumber(await this.command([\"SCARD\", key]));\n }\n\n private async zAdd(\n key: string,\n score: number,\n member: string,\n nx = false,\n ): Promise<void> {\n await this.command([\n \"ZADD\",\n key,\n ...(nx ? [\"NX\"] : []),\n score.toString(),\n member,\n ]);\n }\n\n private async zRem(key: string, member: string): Promise<void> {\n await this.command([\"ZREM\", key, member]);\n }\n\n private async zRange(key: string): Promise<string[]> {\n return toStringArray(await this.command([\"ZRANGE\", key, \"0\", \"-1\"]));\n }\n\n private async zRangeByScore(\n key: string,\n min: string,\n max: string,\n reverse: boolean,\n limit?: number,\n ): Promise<string[]> {\n const command = reverse ? \"ZREVRANGEBYSCORE\" : \"ZRANGEBYSCORE\";\n const args = [command, key, reverse ? max : min, reverse ? min : max];\n if (limit != null) args.push(\"LIMIT\", \"0\", limit.toString());\n return toStringArray(await this.command(args));\n }\n\n private async zCard(key: string): Promise<number> {\n return toNumber(await this.command([\"ZCARD\", key]));\n }\n\n private async nextSequence(key: string): Promise<number> {\n return toNumber(await this.command([\"INCR\", key]));\n }\n\n private async withRedisLock<T>(\n lockKey: string,\n operation: () => Promise<T>,\n ): Promise<T> {\n const token = crypto.randomUUID();\n while (\n await this.command([\n \"SET\",\n lockKey,\n token,\n \"NX\",\n \"PX\",\n redisLockTimeoutMs.toString(),\n ]) !== \"OK\"\n ) {\n await delay(redisLockPollIntervalMs);\n }\n const renew = setInterval(() => {\n void (async () => {\n try {\n await this.command([\n \"EVAL\",\n \"if redis.call('GET', KEYS[1]) == ARGV[1] then \" +\n \"return redis.call('PEXPIRE', KEYS[1], ARGV[2]) else return 0 end\",\n \"1\",\n lockKey,\n token,\n redisLockTimeoutMs.toString(),\n ]);\n } catch (error) {\n logger.warn(\"Failed to renew Redis lock: {error}\", { error });\n }\n })();\n }, redisLockRenewIntervalMs);\n try {\n return await operation();\n } finally {\n clearInterval(renew);\n try {\n await this.command([\n \"EVAL\",\n \"if redis.call('GET', KEYS[1]) == ARGV[1] then \" +\n \"return redis.call('DEL', KEYS[1]) else return 0 end\",\n \"1\",\n lockKey,\n token,\n ]);\n } catch (error) {\n logger.warn(\"Failed to release Redis lock: {error}\", { error });\n }\n }\n }\n\n async setKeyPairs(\n identifier: string,\n keyPairs: CryptoKeyPair[],\n ): Promise<void> {\n const pairs: KeyPair[] = [];\n for (const keyPair of keyPairs) {\n pairs.push({\n private: await exportJwk(keyPair.privateKey),\n public: await exportJwk(keyPair.publicKey),\n });\n }\n await this.set(this.botKey(identifier, \"keyPairs\"), JSON.stringify(pairs));\n }\n\n async getKeyPairs(identifier: string): Promise<CryptoKeyPair[] | undefined> {\n const json = await this.get(this.botKey(identifier, \"keyPairs\"));\n if (json == null) return undefined;\n const pairs = JSON.parse(json) as KeyPair[];\n return await Promise.all(pairs.map(async (pair) => ({\n privateKey: await importJwk(pair.private, \"private\"),\n publicKey: await importJwk(pair.public, \"public\"),\n })));\n }\n\n async addMessage(\n identifier: string,\n id: Uuid,\n activity: Create | Announce,\n ): Promise<void> {\n await this.set(\n this.botKey(identifier, \"messages\", id),\n JSON.stringify(await activity.toJsonLd({ format: \"compact\" })),\n );\n await this.zAdd(\n this.botKey(identifier, \"messages\"),\n getMessageScore(id, activity),\n id,\n );\n }\n\n async updateMessage(\n identifier: string,\n id: Uuid,\n updater: (\n existing: Create | Announce,\n ) => Create | Announce | undefined | Promise<Create | Announce | undefined>,\n ): Promise<boolean> {\n const key = this.botKey(identifier, \"messages\", id);\n return await this.withRedisLock(\n this.botKey(identifier, \"messages\", id, \"lock\"),\n async () => {\n const json = await this.get(key);\n if (json == null) return false;\n const activity = await Activity.fromJsonLd(JSON.parse(json));\n if (!(activity instanceof Create || activity instanceof Announce)) {\n return false;\n }\n const updated = await updater(activity);\n if (updated == null) return false;\n await this.set(\n key,\n JSON.stringify(await updated.toJsonLd({ format: \"compact\" })),\n );\n await this.zAdd(\n this.botKey(identifier, \"messages\"),\n getMessageScore(id, updated),\n id,\n );\n return true;\n },\n );\n }\n\n async removeMessage(\n identifier: string,\n id: Uuid,\n ): Promise<Create | Announce | undefined> {\n const key = this.botKey(identifier, \"messages\", id);\n const json = await this.get(key);\n await this.del(key);\n await this.zRem(this.botKey(identifier, \"messages\"), id);\n if (json == null) return undefined;\n const activity = await Activity.fromJsonLd(JSON.parse(json));\n if (activity instanceof Create || activity instanceof Announce) {\n return activity;\n }\n return undefined;\n }\n\n async *getMessages(\n identifier: string,\n options: RepositoryGetMessagesOptions = {},\n ): AsyncIterable<Create | Announce> {\n const min = options.since == null\n ? \"-inf\"\n : options.since.epochMilliseconds.toString();\n const max = options.until == null\n ? \"+inf\"\n : options.until.epochMilliseconds.toString();\n const ids = await this.zRangeByScore(\n this.botKey(identifier, \"messages\"),\n min,\n max,\n options.order == null || options.order === \"newest\",\n options.limit,\n );\n for (const id of ids) {\n const json = await this.get(this.botKey(identifier, \"messages\", id));\n if (json == null) continue;\n try {\n const activity = await Activity.fromJsonLd(JSON.parse(json));\n if (activity instanceof Create || activity instanceof Announce) {\n yield activity;\n }\n } catch {\n continue;\n }\n }\n }\n\n async getMessage(\n identifier: string,\n id: Uuid,\n ): Promise<Create | Announce | undefined> {\n const json = await this.get(this.botKey(identifier, \"messages\", id));\n if (json == null) return undefined;\n try {\n const activity = await Activity.fromJsonLd(JSON.parse(json));\n if (activity instanceof Create || activity instanceof Announce) {\n return activity;\n }\n } catch {\n return undefined;\n }\n return undefined;\n }\n\n countMessages(identifier: string): Promise<number> {\n return this.zCard(this.botKey(identifier, \"messages\"));\n }\n\n async addFollower(\n identifier: string,\n followId: URL,\n follower: Actor,\n ): Promise<void> {\n if (follower.id == null) {\n throw new TypeError(\"The follower ID is missing.\");\n }\n const followerId = follower.id.href;\n const followIdString = followId.href;\n const requestKey = this.botKey(\n identifier,\n \"followRequests\",\n followIdString,\n );\n await this.withRedisLock(\n this.botKey(identifier, \"followers\", \"lock\"),\n async () => {\n const previousFollowerId = await this.get(requestKey);\n await this.set(\n this.botKey(identifier, \"followers\", followerId),\n JSON.stringify(await follower.toJsonLd({ format: \"compact\" })),\n );\n await this.zAdd(\n this.botKey(identifier, \"followers\"),\n await this.nextSequence(this.botKey(identifier, \"followers\", \"seq\")),\n followerId,\n true,\n );\n await this.sAdd(\n this.botKey(identifier, \"followerRequests\", followerId),\n followIdString,\n );\n await this.set(requestKey, followerId);\n if (previousFollowerId != null && previousFollowerId !== followerId) {\n await this.sRem(\n this.botKey(identifier, \"followerRequests\", previousFollowerId),\n followIdString,\n );\n await this.cleanupFollower(identifier, previousFollowerId);\n }\n },\n );\n }\n\n async removeFollower(\n identifier: string,\n followId: URL,\n followerId: URL,\n ): Promise<Actor | undefined> {\n const followIdString = followId.href;\n const requestKey = this.botKey(\n identifier,\n \"followRequests\",\n followIdString,\n );\n return await this.withRedisLock(\n this.botKey(identifier, \"followers\", \"lock\"),\n async () => {\n const currentFollowerId = await this.get(requestKey);\n if (currentFollowerId !== followerId.href) return undefined;\n const followerKey = this.botKey(\n identifier,\n \"followers\",\n followerId.href,\n );\n const json = await this.get(followerKey);\n await this.del(requestKey);\n await this.sRem(\n this.botKey(identifier, \"followerRequests\", followerId.href),\n followIdString,\n );\n const removed = await this.cleanupFollower(identifier, followerId.href);\n if (!removed || json == null) return undefined;\n try {\n const actor = await Object.fromJsonLd(JSON.parse(json));\n if (isActor(actor)) return actor;\n } catch {\n return undefined;\n }\n return undefined;\n },\n );\n }\n\n private async cleanupFollower(\n identifier: string,\n followerId: string,\n ): Promise<boolean> {\n const requestsKey = this.botKey(identifier, \"followerRequests\", followerId);\n if (await this.sCard(requestsKey) > 0) return false;\n await this.del(\n requestsKey,\n this.botKey(identifier, \"followers\", followerId),\n );\n await this.zRem(this.botKey(identifier, \"followers\"), followerId);\n return true;\n }\n\n async hasFollower(identifier: string, followerId: URL): Promise<boolean> {\n return await this.get(\n this.botKey(identifier, \"followers\", followerId.href),\n ) !=\n null;\n }\n\n async *getFollowers(\n identifier: string,\n options: RepositoryGetFollowersOptions = {},\n ): AsyncIterable<Actor> {\n const start = options.offset ?? 0;\n const stop = options.limit == null ? -1 : start + options.limit - 1;\n const ids = toStringArray(\n await this.command([\n \"ZRANGE\",\n this.botKey(identifier, \"followers\"),\n start.toString(),\n stop.toString(),\n ]),\n );\n for (const id of ids) {\n const json = await this.get(this.botKey(identifier, \"followers\", id));\n if (json == null) continue;\n try {\n const actor = await Object.fromJsonLd(JSON.parse(json));\n if (isActor(actor)) yield actor;\n } catch {\n continue;\n }\n }\n }\n\n countFollowers(identifier: string): Promise<number> {\n return this.zCard(this.botKey(identifier, \"followers\"));\n }\n\n async addSentFollow(\n identifier: string,\n id: Uuid,\n follow: Follow,\n ): Promise<void> {\n await this.set(\n this.botKey(identifier, \"follows\", id),\n JSON.stringify(await follow.toJsonLd({ format: \"compact\" })),\n );\n }\n\n async removeSentFollow(\n identifier: string,\n id: Uuid,\n ): Promise<Follow | undefined> {\n const follow = await this.getSentFollow(identifier, id);\n if (follow == null) return undefined;\n await this.del(this.botKey(identifier, \"follows\", id));\n return follow;\n }\n\n async getSentFollow(\n identifier: string,\n id: Uuid,\n ): Promise<Follow | undefined> {\n const json = await this.get(this.botKey(identifier, \"follows\", id));\n if (json == null) return undefined;\n try {\n return await Follow.fromJsonLd(JSON.parse(json));\n } catch {\n return undefined;\n }\n }\n\n async addFollowee(\n identifier: string,\n followeeId: URL,\n follow: Follow,\n ): Promise<void> {\n await this.set(\n this.botKey(identifier, \"followees\", followeeId.href),\n JSON.stringify(await follow.toJsonLd({ format: \"compact\" })),\n );\n await this.zAdd(\n this.key(\"index\", \"followees\", followeeId.href),\n 0,\n identifier,\n );\n }\n\n async removeFollowee(\n identifier: string,\n followeeId: URL,\n ): Promise<Follow | undefined> {\n const follow = await this.getFollowee(identifier, followeeId);\n await this.del(this.botKey(identifier, \"followees\", followeeId.href));\n await this.zRem(\n this.key(\"index\", \"followees\", followeeId.href),\n identifier,\n );\n return follow;\n }\n\n async getFollowee(\n identifier: string,\n followeeId: URL,\n ): Promise<Follow | undefined> {\n const json = await this.get(\n this.botKey(identifier, \"followees\", followeeId.href),\n );\n if (json == null) return undefined;\n try {\n return await Follow.fromJsonLd(JSON.parse(json));\n } catch {\n return undefined;\n }\n }\n\n async *findFollowedBots(followeeId: URL): AsyncIterable<string> {\n yield* await this.zRange(this.key(\"index\", \"followees\", followeeId.href));\n }\n\n async addQuoteAuthorization(\n identifier: string,\n id: Uuid,\n authorization: QuoteAuthorization,\n ): Promise<void> {\n const interactingObject = authorization.interactingObjectId;\n if (interactingObject == null) {\n throw new TypeError(\n \"The quote authorization interacting object is missing.\",\n );\n }\n const indexKey = this.botKey(\n identifier,\n \"quoteAuthorizationsByInteractingObject\",\n interactingObject.href,\n );\n await this.withRedisLock(\n this.quoteAuthorizationLockKey(indexKey),\n async () => {\n const existing = await this.get(indexKey);\n if (existing != null) {\n if (\n await this.getQuoteAuthorization(identifier, existing as Uuid) !=\n null\n ) {\n return;\n }\n await this.del(indexKey);\n }\n await this.set(\n this.botKey(identifier, \"quoteAuthorizations\", id),\n JSON.stringify(await authorization.toJsonLd({ format: \"compact\" })),\n );\n await this.set(indexKey, id);\n },\n );\n }\n\n async getQuoteAuthorization(\n identifier: string,\n id: Uuid,\n ): Promise<QuoteAuthorization | undefined> {\n const json = await this.get(\n this.botKey(identifier, \"quoteAuthorizations\", id),\n );\n if (json == null) return undefined;\n try {\n return await QuoteAuthorization.fromJsonLd(JSON.parse(json));\n } catch {\n return undefined;\n }\n }\n\n async findQuoteAuthorization(\n identifier: string,\n interactingObject: URL,\n ): Promise<QuoteAuthorization | undefined> {\n const indexKey = this.botKey(\n identifier,\n \"quoteAuthorizationsByInteractingObject\",\n interactingObject.href,\n );\n return await this.withRedisLock(\n this.quoteAuthorizationLockKey(indexKey),\n async () => {\n const id = await this.get(indexKey);\n if (id == null) return undefined;\n const authorization = await this.getQuoteAuthorization(\n identifier,\n id as Uuid,\n );\n if (authorization == null && await this.get(indexKey) === id) {\n await this.del(indexKey);\n }\n return authorization;\n },\n );\n }\n\n async removeQuoteAuthorization(\n identifier: string,\n id: Uuid,\n ): Promise<QuoteAuthorization | undefined> {\n const key = this.botKey(identifier, \"quoteAuthorizations\", id);\n const json = await this.get(key);\n await this.del(key);\n if (json == null) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(json);\n } catch {\n return undefined;\n }\n const interactingObject = getRawQuoteAuthorizationInteractingObject(parsed);\n if (interactingObject != null) {\n const indexKey = this.botKey(\n identifier,\n \"quoteAuthorizationsByInteractingObject\",\n interactingObject.href,\n );\n await this.withRedisLock(\n this.quoteAuthorizationLockKey(indexKey),\n async () => {\n if (await this.get(indexKey) === id) await this.del(indexKey);\n },\n );\n }\n try {\n return await QuoteAuthorization.fromJsonLd(parsed);\n } catch {\n return undefined;\n }\n }\n\n async addQuoteAuthorizationReference(\n identifier: string,\n authorization: URL,\n messageId: Uuid,\n attribution?: URL,\n ): Promise<void> {\n const value: QuoteAuthorizationReferenceData = attribution == null\n ? { messageId }\n : { messageId, attribution: attribution.href };\n await this.set(\n this.botKey(identifier, \"quoteAuthorizationRefs\", authorization.href),\n JSON.stringify(value),\n );\n await this.zAdd(\n this.key(\"index\", \"quoteAuthorizationRefs\", authorization.href),\n 0,\n identifier,\n );\n }\n\n async findQuoteAuthorizationReference(\n identifier: string,\n authorization: URL,\n ): Promise<Uuid | undefined> {\n const json = await this.get(\n this.botKey(identifier, \"quoteAuthorizationRefs\", authorization.href),\n );\n if (json == null) return undefined;\n try {\n const value = JSON.parse(json) as QuoteAuthorizationReferenceData;\n return value.messageId;\n } catch {\n return undefined;\n }\n }\n\n async *findQuoteAuthorizationReferenceIdentifiers(\n authorization: URL,\n ): AsyncIterable<string> {\n yield* await this.zRange(\n this.key(\"index\", \"quoteAuthorizationRefs\", authorization.href),\n );\n }\n\n async findQuoteAuthorizationReferenceAttribution(\n identifier: string,\n authorization: URL,\n ): Promise<URL | undefined> {\n const json = await this.get(\n this.botKey(identifier, \"quoteAuthorizationRefs\", authorization.href),\n );\n if (json == null) return undefined;\n let value: QuoteAuthorizationReferenceData;\n try {\n value = JSON.parse(json) as QuoteAuthorizationReferenceData;\n } catch {\n return undefined;\n }\n if (value.attribution == null) return undefined;\n try {\n return new URL(value.attribution);\n } catch {\n return undefined;\n }\n }\n\n async removeQuoteAuthorizationReference(\n identifier: string,\n authorization: URL,\n ): Promise<void> {\n await this.del(\n this.botKey(identifier, \"quoteAuthorizationRefs\", authorization.href),\n );\n await this.zRem(\n this.key(\"index\", \"quoteAuthorizationRefs\", authorization.href),\n identifier,\n );\n }\n\n async vote(\n identifier: string,\n messageId: Uuid,\n voterId: URL,\n option: string,\n ): Promise<void> {\n await this.sAdd(\n this.botKey(identifier, \"polls\", messageId, \"voters\"),\n voterId.href,\n );\n await this.sAdd(\n this.botKey(identifier, \"polls\", messageId, \"options\", option),\n voterId.href,\n );\n await this.zAdd(\n this.botKey(identifier, \"polls\", messageId, \"options\"),\n 0,\n option,\n );\n }\n\n countVoters(identifier: string, messageId: Uuid): Promise<number> {\n return this.sCard(this.botKey(identifier, \"polls\", messageId, \"voters\"));\n }\n\n async countVotes(\n identifier: string,\n messageId: Uuid,\n ): Promise<Readonly<Record<string, number>>> {\n const result: Record<string, number> = {};\n const options = await this.zRange(\n this.botKey(identifier, \"polls\", messageId, \"options\"),\n );\n for (const option of options) {\n result[option] = await this.sCard(\n this.botKey(identifier, \"polls\", messageId, \"options\", option),\n );\n }\n return result;\n }\n\n forIdentifier(identifier: string): ActorScopedRepository {\n return new ActorScopedRepository(this, identifier);\n }\n\n private quoteAuthorizationLockKey(indexKey: string): string {\n return `${indexKey}:lock`;\n }\n}\n\nfunction toNumber(value: unknown): number {\n if (typeof value === \"number\") return value;\n if (typeof value === \"bigint\") return Number(value);\n if (typeof value === \"string\") return Number.parseInt(value, 10);\n return 0;\n}\n\nfunction toStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is string => typeof item === \"string\");\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction getMessageScore(id: Uuid, activity: Create | Announce): number {\n if (activity.published != null) return activity.published.epochMilliseconds;\n return extractTimestamp(id);\n}\n\nfunction extractTimestamp(uuid: string): number {\n const match = uuidV7Pattern.exec(uuid);\n if (match == null) return 0;\n return Number.parseInt(`${match[1]}${match[2]}`, 16);\n}\n\nfunction getRawQuoteAuthorizationInteractingObject(\n json: unknown,\n): URL | undefined {\n if (typeof json !== \"object\" || json == null) return undefined;\n if (!(\"interactingObject\" in json)) return undefined;\n const interactingObject = json.interactingObject;\n if (typeof interactingObject !== \"string\") return undefined;\n try {\n return new URL(interactingObject);\n } catch {\n return undefined;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"mod.js","names":["options: RedisRepositoryOptions","client: RedisClientLike","identifier: string","args: readonly string[]","key: string","keys: readonly string[]","chunks: string[][]","value: string","member: string","score: number","min: string","max: string","reverse: boolean","limit?: number","lockKey: string","operation: () => Promise<T>","keyPairs: CryptoKeyPair[]","pairs: KeyPair[]","id: Uuid","activity: Create | Announce","updater: (\n existing: Create | Announce,\n ) => Create | Announce | undefined | Promise<Create | Announce | undefined>","activity: Activity","options: RepositoryGetMessagesOptions","followId: URL","follower: Actor","followerId: URL","followerId: string","options: RepositoryGetFollowersOptions","follow: Follow","followeeId: URL","authorization: QuoteAuthorization","interactingObject: URL","parsed: unknown","parsed","authorization: URL","messageId: Uuid","attribution?: URL","value: QuoteAuthorizationReferenceData","voterId: URL","option: string","result: Record<string, number>","indexKey: string","value: unknown","name: string","value: number","ms: number","uuid: string","json: unknown"],"sources":["../src/mod.ts"],"sourcesContent":["// BotKit by Fedify: A framework for creating ActivityPub bots\n// Copyright (C) 2026 Hong Minhee <https://hongminhee.org/>\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\nimport {\n ActorScopedRepository,\n type Repository,\n type RepositoryGetFollowersOptions,\n type RepositoryGetMessagesOptions,\n type Uuid,\n} from \"@fedify/botkit/repository\";\nimport { exportJwk, importJwk } from \"@fedify/fedify/sig\";\nimport {\n Activity,\n type Actor,\n Announce,\n Create,\n Follow,\n isActor,\n Object,\n QuoteAuthorization,\n} from \"@fedify/vocab\";\nimport { getLogger } from \"@logtape/logtape\";\nimport { createClient, type RedisClientOptions } from \"redis\";\n\nconst logger = getLogger([\"botkit\", \"redis\"]);\nconst defaultRedisLockTimeoutMs = 30_000;\nconst defaultRedisLockPollIntervalMs = 20;\nconst defaultRedisLockRenewIntervalMs = 10_000;\nconst redisMGetBatchSize = 100;\nconst uuidV7Pattern =\n /^([0-9a-f]{8})-([0-9a-f]{4})-7[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\ninterface RedisClientLike {\n readonly isOpen?: boolean;\n connect?(): Promise<unknown>;\n quit?(): Promise<unknown>;\n close?(): Promise<unknown> | unknown;\n on?(event: \"error\", listener: (error: unknown) => void): unknown;\n sendCommand(args: readonly string[]): Promise<unknown>;\n}\n\ninterface KeyPair {\n readonly private: JsonWebKey;\n readonly public: JsonWebKey;\n}\n\ninterface QuoteAuthorizationReferenceData {\n readonly messageId: Uuid;\n readonly attribution?: string;\n}\n\ninterface RedisRepositoryOptionsBase {\n /**\n * The Redis key prefix under which all BotKit data is stored.\n * @default `\"botkit\"`\n */\n readonly prefix?: string;\n\n /**\n * How long a Redis lock can live without renewal, in milliseconds.\n * @default `30000`\n */\n readonly lockTimeoutMs?: number;\n\n /**\n * How long to wait before retrying a held Redis lock, in milliseconds.\n * @default `20`\n */\n readonly lockPollIntervalMs?: number;\n\n /**\n * How often to renew a held Redis lock, in milliseconds.\n * @default `10000`\n */\n readonly lockRenewIntervalMs?: number;\n}\n\ninterface RedisRepositoryOptionsWithClient extends RedisRepositoryOptionsBase {\n /**\n * A pre-configured Redis client to use. It must already be connected.\n */\n readonly client: RedisClientLike;\n\n /**\n * Disallowed when `client` is provided.\n */\n readonly url?: never;\n\n /**\n * Disallowed when `client` is provided.\n */\n readonly clientOptions?: never;\n}\n\ninterface RedisRepositoryOptionsWithUrl extends RedisRepositoryOptionsBase {\n /**\n * A Redis connection string to connect with.\n */\n readonly url: string | URL;\n\n /**\n * Disallowed when `url` is provided.\n */\n readonly client?: never;\n\n /**\n * Additional node-redis client options.\n */\n readonly clientOptions?: Omit<RedisClientOptions, \"url\">;\n}\n\n/**\n * Options for creating a Redis repository.\n * @since 0.5.0\n */\nexport type RedisRepositoryOptions =\n | RedisRepositoryOptionsWithClient\n | RedisRepositoryOptionsWithUrl;\n\n/**\n * A repository for storing bot data using Redis.\n * @since 0.5.0\n */\nexport class RedisRepository implements Repository, AsyncDisposable {\n private readonly client: RedisClientLike;\n private readonly ownsClient: boolean;\n private ready: Promise<unknown> | undefined;\n private readonly lockTimeoutMs: number;\n private readonly lockPollIntervalMs: number;\n private readonly lockRenewIntervalMs: number;\n private closed = false;\n\n /**\n * The Redis key prefix under which all BotKit data is stored.\n */\n readonly prefix: string;\n\n /**\n * Creates a new Redis repository.\n * @param options The options for creating the repository.\n * @throws {TypeError} If exactly one of `client` and `url` is not provided.\n * @throws {RangeError} If a lock timing option is not a positive number, or\n * if `lockRenewIntervalMs` is not less than `lockTimeoutMs`.\n */\n constructor(options: RedisRepositoryOptions) {\n if (options == null) {\n throw new TypeError(\"RedisRepositoryOptions must be provided.\");\n }\n const hasClient = \"client\" in options && options.client != null;\n const hasUrl = \"url\" in options && options.url != null;\n if (hasClient === hasUrl) {\n throw new TypeError(\n \"RedisRepositoryOptions must provide exactly one of client or url.\",\n );\n }\n this.prefix = options.prefix ?? \"botkit\";\n this.lockTimeoutMs = options.lockTimeoutMs ?? defaultRedisLockTimeoutMs;\n this.lockPollIntervalMs = options.lockPollIntervalMs ??\n defaultRedisLockPollIntervalMs;\n this.lockRenewIntervalMs = options.lockRenewIntervalMs ??\n defaultRedisLockRenewIntervalMs;\n validatePositiveMilliseconds(\"lockTimeoutMs\", this.lockTimeoutMs);\n validatePositiveMilliseconds(\n \"lockPollIntervalMs\",\n this.lockPollIntervalMs,\n );\n validatePositiveMilliseconds(\n \"lockRenewIntervalMs\",\n this.lockRenewIntervalMs,\n );\n if (this.lockRenewIntervalMs >= this.lockTimeoutMs) {\n throw new RangeError(\n \"lockRenewIntervalMs must be less than lockTimeoutMs.\",\n );\n }\n if (hasClient) {\n this.client = options.client;\n this.ownsClient = false;\n } else {\n const client: RedisClientLike = createClient({\n ...options.clientOptions,\n url: options.url.toString(),\n });\n this.client = client;\n this.ownsClient = true;\n client.on?.(\"error\", (error) => {\n logger.warn(\"Owned Redis client emitted an error: {error}\", { error });\n });\n const ready = this.connect();\n ready.catch(() => {\n if (this.ready === ready) this.ready = undefined;\n });\n }\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.close();\n }\n\n /**\n * Closes the owned Redis connection.\n */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n if (!this.ownsClient) return;\n const ready = this.ready;\n try {\n await ready;\n } catch (error) {\n logger.warn(\n \"Owned Redis client did not become ready before close: {error}\",\n {\n error,\n },\n );\n if (this.ready === ready) this.ready = undefined;\n }\n try {\n if (this.client.quit != null) {\n await this.client.quit();\n } else {\n await this.client.close?.();\n }\n } catch (error) {\n logger.warn(\"Failed to close owned Redis client: {error}\", { error });\n }\n }\n\n private key(...segments: readonly string[]): string {\n return [\n this.prefix,\n ...segments.map((segment) => encodeURIComponent(segment)),\n ].join(\":\");\n }\n\n private botKey(identifier: string, ...segments: readonly string[]): string {\n return this.key(\"bots\", identifier, ...segments);\n }\n\n private connect(): Promise<unknown> {\n const ready = this.client.connect?.() ?? Promise.resolve();\n this.ready = ready;\n return ready;\n }\n\n private async ensureReady(): Promise<void> {\n if (this.closed) {\n throw new TypeError(\"RedisRepository is closed.\");\n }\n if (!this.ownsClient) return;\n const ready = this.ready;\n if (ready == null) {\n if (this.client.isOpen) return;\n const nextReady = this.connect();\n try {\n await nextReady;\n } catch (error) {\n if (this.ready === nextReady) this.ready = undefined;\n throw error;\n }\n return;\n }\n try {\n await ready;\n } catch (error) {\n if (this.ready === ready) this.ready = undefined;\n throw error;\n }\n }\n\n private async command(args: readonly string[]): Promise<unknown> {\n await this.ensureReady();\n try {\n return await this.client.sendCommand(args);\n } catch (error) {\n logger.error(\"Redis command failed: {command}.\", {\n command: args[0],\n error,\n });\n throw error;\n }\n }\n\n private async get(key: string): Promise<string | undefined> {\n const value = await this.command([\"GET\", key]);\n return typeof value === \"string\" ? value : undefined;\n }\n\n private async mGet(keys: readonly string[]): Promise<string[]> {\n if (keys.length < 1) return [];\n const chunks: string[][] = [];\n for (let i = 0; i < keys.length; i += redisMGetBatchSize) {\n chunks.push(keys.slice(i, i + redisMGetBatchSize));\n }\n const replies = await Promise.all(\n chunks.map((chunk) => this.command([\"MGET\", ...chunk])),\n );\n return replies.flatMap(toStringArray);\n }\n\n private async set(key: string, value: string): Promise<void> {\n await this.command([\"SET\", key, value]);\n }\n\n private async del(...keys: readonly string[]): Promise<void> {\n if (keys.length < 1) return;\n await this.command([\"DEL\", ...keys]);\n }\n\n private async sAdd(key: string, member: string): Promise<void> {\n await this.command([\"SADD\", key, member]);\n }\n\n private async sRem(key: string, member: string): Promise<void> {\n await this.command([\"SREM\", key, member]);\n }\n\n private async sCard(key: string): Promise<number> {\n return toNumber(await this.command([\"SCARD\", key]));\n }\n\n private async zAdd(\n key: string,\n score: number,\n member: string,\n nx = false,\n ): Promise<void> {\n await this.command([\n \"ZADD\",\n key,\n ...(nx ? [\"NX\"] : []),\n score.toString(),\n member,\n ]);\n }\n\n private async zRem(key: string, member: string): Promise<void> {\n await this.command([\"ZREM\", key, member]);\n }\n\n private async zRange(key: string): Promise<string[]> {\n return toStringArray(await this.command([\"ZRANGE\", key, \"0\", \"-1\"]));\n }\n\n private async zRangeByScore(\n key: string,\n min: string,\n max: string,\n reverse: boolean,\n limit?: number,\n ): Promise<string[]> {\n const command = reverse ? \"ZREVRANGEBYSCORE\" : \"ZRANGEBYSCORE\";\n const args = [command, key, reverse ? max : min, reverse ? min : max];\n if (limit != null) args.push(\"LIMIT\", \"0\", limit.toString());\n return toStringArray(await this.command(args));\n }\n\n private async zCard(key: string): Promise<number> {\n return toNumber(await this.command([\"ZCARD\", key]));\n }\n\n private async nextSequence(key: string): Promise<number> {\n return toNumber(await this.command([\"INCR\", key]));\n }\n\n private async withRedisLock<T>(\n lockKey: string,\n operation: () => Promise<T>,\n ): Promise<T> {\n const token = crypto.randomUUID();\n while (\n await this.command([\n \"SET\",\n lockKey,\n token,\n \"NX\",\n \"PX\",\n this.lockTimeoutMs.toString(),\n ]) !== \"OK\"\n ) {\n await delay(this.lockPollIntervalMs);\n }\n const renew = setInterval(() => {\n void (async () => {\n try {\n if (this.closed) {\n clearInterval(renew);\n return;\n }\n const renewed = toNumber(\n await this.command([\n \"EVAL\",\n \"if redis.call('GET', KEYS[1]) == ARGV[1] then \" +\n \"return redis.call('PEXPIRE', KEYS[1], ARGV[2]) else return 0 end\",\n \"1\",\n lockKey,\n token,\n this.lockTimeoutMs.toString(),\n ]),\n );\n if (renewed === 0) clearInterval(renew);\n } catch (error) {\n if (this.closed) {\n clearInterval(renew);\n } else {\n logger.warn(\"Failed to renew Redis lock: {error}\", { error });\n }\n }\n })();\n }, this.lockRenewIntervalMs);\n try {\n return await operation();\n } finally {\n clearInterval(renew);\n try {\n await this.command([\n \"EVAL\",\n \"if redis.call('GET', KEYS[1]) == ARGV[1] then \" +\n \"return redis.call('DEL', KEYS[1]) else return 0 end\",\n \"1\",\n lockKey,\n token,\n ]);\n } catch (error) {\n logger.warn(\"Failed to release Redis lock: {error}\", { error });\n }\n }\n }\n\n async setKeyPairs(\n identifier: string,\n keyPairs: CryptoKeyPair[],\n ): Promise<void> {\n const pairs: KeyPair[] = await Promise.all(\n keyPairs.map(async (keyPair) => ({\n private: await exportJwk(keyPair.privateKey),\n public: await exportJwk(keyPair.publicKey),\n })),\n );\n await this.set(this.botKey(identifier, \"keyPairs\"), JSON.stringify(pairs));\n }\n\n async getKeyPairs(identifier: string): Promise<CryptoKeyPair[] | undefined> {\n const json = await this.get(this.botKey(identifier, \"keyPairs\"));\n if (json == null) return undefined;\n const pairs = JSON.parse(json) as KeyPair[];\n return await Promise.all(pairs.map(async (pair) => ({\n privateKey: await importJwk(pair.private, \"private\"),\n publicKey: await importJwk(pair.public, \"public\"),\n })));\n }\n\n async addMessage(\n identifier: string,\n id: Uuid,\n activity: Create | Announce,\n ): Promise<void> {\n await this.withRedisLock(\n this.botKey(identifier, \"messages\", id, \"lock\"),\n async () => {\n await this.set(\n this.botKey(identifier, \"messages\", id),\n JSON.stringify(await activity.toJsonLd({ format: \"compact\" })),\n );\n await this.zAdd(\n this.botKey(identifier, \"messages\"),\n getMessageScore(id, activity),\n id,\n );\n },\n );\n }\n\n async updateMessage(\n identifier: string,\n id: Uuid,\n updater: (\n existing: Create | Announce,\n ) => Create | Announce | undefined | Promise<Create | Announce | undefined>,\n ): Promise<boolean> {\n const key = this.botKey(identifier, \"messages\", id);\n return await this.withRedisLock(\n this.botKey(identifier, \"messages\", id, \"lock\"),\n async () => {\n const json = await this.get(key);\n if (json == null) return false;\n let activity: Activity;\n try {\n activity = await Activity.fromJsonLd(JSON.parse(json));\n } catch {\n return false;\n }\n if (!(activity instanceof Create || activity instanceof Announce)) {\n return false;\n }\n const updated = await updater(activity);\n if (updated == null) return false;\n await this.set(\n key,\n JSON.stringify(await updated.toJsonLd({ format: \"compact\" })),\n );\n await this.zAdd(\n this.botKey(identifier, \"messages\"),\n getMessageScore(id, updated),\n id,\n );\n return true;\n },\n );\n }\n\n async removeMessage(\n identifier: string,\n id: Uuid,\n ): Promise<Create | Announce | undefined> {\n const key = this.botKey(identifier, \"messages\", id);\n return await this.withRedisLock(\n this.botKey(identifier, \"messages\", id, \"lock\"),\n async () => {\n const json = await this.get(key);\n await this.del(key);\n await this.zRem(this.botKey(identifier, \"messages\"), id);\n if (json == null) return undefined;\n try {\n const activity = await Activity.fromJsonLd(JSON.parse(json));\n if (activity instanceof Create || activity instanceof Announce) {\n return activity;\n }\n } catch {\n return undefined;\n }\n return undefined;\n },\n );\n }\n\n async *getMessages(\n identifier: string,\n options: RepositoryGetMessagesOptions = {},\n ): AsyncIterable<Create | Announce> {\n const min = options.since == null\n ? \"-inf\"\n : options.since.epochMilliseconds.toString();\n const max = options.until == null\n ? \"+inf\"\n : options.until.epochMilliseconds.toString();\n const ids = await this.zRangeByScore(\n this.botKey(identifier, \"messages\"),\n min,\n max,\n options.order == null || options.order === \"newest\",\n options.limit,\n );\n if (ids.length < 1) return;\n const keys = ids.map((id) => this.botKey(identifier, \"messages\", id));\n const jsons = await this.mGet(keys);\n for (const json of jsons) {\n try {\n const activity = await Activity.fromJsonLd(JSON.parse(json));\n if (activity instanceof Create || activity instanceof Announce) {\n yield activity;\n }\n } catch {\n continue;\n }\n }\n }\n\n async getMessage(\n identifier: string,\n id: Uuid,\n ): Promise<Create | Announce | undefined> {\n const json = await this.get(this.botKey(identifier, \"messages\", id));\n if (json == null) return undefined;\n try {\n const activity = await Activity.fromJsonLd(JSON.parse(json));\n if (activity instanceof Create || activity instanceof Announce) {\n return activity;\n }\n } catch {\n return undefined;\n }\n return undefined;\n }\n\n countMessages(identifier: string): Promise<number> {\n return this.zCard(this.botKey(identifier, \"messages\"));\n }\n\n /**\n * Adds a follower for a follow request.\n *\n * @throws {TypeError} If the follower actor has no ID.\n */\n async addFollower(\n identifier: string,\n followId: URL,\n follower: Actor,\n ): Promise<void> {\n if (follower.id == null) {\n throw new TypeError(\"The follower ID is missing.\");\n }\n const followerId = follower.id.href;\n const followIdString = followId.href;\n const requestKey = this.botKey(\n identifier,\n \"followRequests\",\n followIdString,\n );\n await this.withRedisLock(\n this.botKey(identifier, \"followers\", \"lock\"),\n async () => {\n const previousFollowerId = await this.get(requestKey);\n await this.set(\n this.botKey(identifier, \"followers\", followerId),\n JSON.stringify(await follower.toJsonLd({ format: \"compact\" })),\n );\n await this.zAdd(\n this.botKey(identifier, \"followers\"),\n await this.nextSequence(this.botKey(identifier, \"followers\", \"seq\")),\n followerId,\n true,\n );\n await this.sAdd(\n this.botKey(identifier, \"followerRequests\", followerId),\n followIdString,\n );\n await this.set(requestKey, followerId);\n if (previousFollowerId != null && previousFollowerId !== followerId) {\n await this.sRem(\n this.botKey(identifier, \"followerRequests\", previousFollowerId),\n followIdString,\n );\n await this.cleanupFollower(identifier, previousFollowerId);\n }\n },\n );\n }\n\n async removeFollower(\n identifier: string,\n followId: URL,\n followerId: URL,\n ): Promise<Actor | undefined> {\n const followIdString = followId.href;\n const requestKey = this.botKey(\n identifier,\n \"followRequests\",\n followIdString,\n );\n return await this.withRedisLock(\n this.botKey(identifier, \"followers\", \"lock\"),\n async () => {\n const currentFollowerId = await this.get(requestKey);\n if (\n currentFollowerId != null && currentFollowerId !== followerId.href\n ) {\n return undefined;\n }\n const followerKey = this.botKey(\n identifier,\n \"followers\",\n followerId.href,\n );\n const json = await this.get(followerKey);\n if (currentFollowerId === followerId.href) await this.del(requestKey);\n await this.sRem(\n this.botKey(identifier, \"followerRequests\", followerId.href),\n followIdString,\n );\n const removed = await this.cleanupFollower(identifier, followerId.href);\n if (!removed || json == null) return undefined;\n try {\n const actor = await Object.fromJsonLd(JSON.parse(json));\n if (isActor(actor)) return actor;\n } catch {\n return undefined;\n }\n return undefined;\n },\n );\n }\n\n private async cleanupFollower(\n identifier: string,\n followerId: string,\n ): Promise<boolean> {\n const requestsKey = this.botKey(identifier, \"followerRequests\", followerId);\n if (await this.sCard(requestsKey) > 0) return false;\n await this.del(\n requestsKey,\n this.botKey(identifier, \"followers\", followerId),\n );\n await this.zRem(this.botKey(identifier, \"followers\"), followerId);\n return true;\n }\n\n async hasFollower(identifier: string, followerId: URL): Promise<boolean> {\n return await this.get(\n this.botKey(identifier, \"followers\", followerId.href),\n ) !=\n null;\n }\n\n async *getFollowers(\n identifier: string,\n options: RepositoryGetFollowersOptions = {},\n ): AsyncIterable<Actor> {\n if (options.limit === 0) return;\n const start = options.offset ?? 0;\n const stop = options.limit == null ? -1 : start + options.limit - 1;\n const ids = toStringArray(\n await this.command([\n \"ZRANGE\",\n this.botKey(identifier, \"followers\"),\n start.toString(),\n stop.toString(),\n ]),\n );\n if (ids.length < 1) return;\n const keys = ids.map((id) => this.botKey(identifier, \"followers\", id));\n const jsons = await this.mGet(keys);\n for (const json of jsons) {\n try {\n const actor = await Object.fromJsonLd(JSON.parse(json));\n if (isActor(actor)) yield actor;\n } catch {\n continue;\n }\n }\n }\n\n countFollowers(identifier: string): Promise<number> {\n return this.zCard(this.botKey(identifier, \"followers\"));\n }\n\n async addSentFollow(\n identifier: string,\n id: Uuid,\n follow: Follow,\n ): Promise<void> {\n await this.set(\n this.botKey(identifier, \"follows\", id),\n JSON.stringify(await follow.toJsonLd({ format: \"compact\" })),\n );\n }\n\n async removeSentFollow(\n identifier: string,\n id: Uuid,\n ): Promise<Follow | undefined> {\n const follow = await this.getSentFollow(identifier, id);\n if (follow == null) return undefined;\n await this.del(this.botKey(identifier, \"follows\", id));\n return follow;\n }\n\n async getSentFollow(\n identifier: string,\n id: Uuid,\n ): Promise<Follow | undefined> {\n const json = await this.get(this.botKey(identifier, \"follows\", id));\n if (json == null) return undefined;\n try {\n return await Follow.fromJsonLd(JSON.parse(json));\n } catch {\n return undefined;\n }\n }\n\n async addFollowee(\n identifier: string,\n followeeId: URL,\n follow: Follow,\n ): Promise<void> {\n await this.withRedisLock(\n this.botKey(identifier, \"followees\", followeeId.href, \"lock\"),\n async () => {\n await this.set(\n this.botKey(identifier, \"followees\", followeeId.href),\n JSON.stringify(await follow.toJsonLd({ format: \"compact\" })),\n );\n await this.zAdd(\n this.key(\"index\", \"followees\", followeeId.href),\n 0,\n identifier,\n );\n },\n );\n }\n\n async removeFollowee(\n identifier: string,\n followeeId: URL,\n ): Promise<Follow | undefined> {\n return await this.withRedisLock(\n this.botKey(identifier, \"followees\", followeeId.href, \"lock\"),\n async () => {\n const follow = await this.getFollowee(identifier, followeeId);\n await this.del(this.botKey(identifier, \"followees\", followeeId.href));\n await this.zRem(\n this.key(\"index\", \"followees\", followeeId.href),\n identifier,\n );\n return follow;\n },\n );\n }\n\n async getFollowee(\n identifier: string,\n followeeId: URL,\n ): Promise<Follow | undefined> {\n const json = await this.get(\n this.botKey(identifier, \"followees\", followeeId.href),\n );\n if (json == null) return undefined;\n try {\n return await Follow.fromJsonLd(JSON.parse(json));\n } catch {\n return undefined;\n }\n }\n\n async *findFollowedBots(followeeId: URL): AsyncIterable<string> {\n yield* await this.zRange(this.key(\"index\", \"followees\", followeeId.href));\n }\n\n /**\n * Stores a quote authorization.\n *\n * @throws {TypeError} If the quote authorization has no interacting object.\n */\n async addQuoteAuthorization(\n identifier: string,\n id: Uuid,\n authorization: QuoteAuthorization,\n ): Promise<void> {\n const interactingObject = authorization.interactingObjectId;\n if (interactingObject == null) {\n throw new TypeError(\n \"The quote authorization interacting object is missing.\",\n );\n }\n const indexKey = this.botKey(\n identifier,\n \"quoteAuthorizationsByInteractingObject\",\n interactingObject.href,\n );\n await this.withRedisLock(\n this.quoteAuthorizationLockKey(indexKey),\n async () => {\n const existing = await this.get(indexKey);\n if (existing != null) {\n if (\n await this.getQuoteAuthorization(identifier, existing as Uuid) !=\n null\n ) {\n return;\n }\n await this.del(\n indexKey,\n this.botKey(\n identifier,\n \"quoteAuthorizationInteractingObjects\",\n existing,\n ),\n );\n }\n await this.set(\n this.botKey(identifier, \"quoteAuthorizations\", id),\n JSON.stringify(await authorization.toJsonLd({ format: \"compact\" })),\n );\n await this.set(\n this.botKey(identifier, \"quoteAuthorizationInteractingObjects\", id),\n interactingObject.href,\n );\n await this.set(indexKey, id);\n },\n );\n }\n\n async getQuoteAuthorization(\n identifier: string,\n id: Uuid,\n ): Promise<QuoteAuthorization | undefined> {\n const json = await this.get(\n this.botKey(identifier, \"quoteAuthorizations\", id),\n );\n if (json == null) return undefined;\n try {\n return await QuoteAuthorization.fromJsonLd(JSON.parse(json));\n } catch {\n return undefined;\n }\n }\n\n async findQuoteAuthorization(\n identifier: string,\n interactingObject: URL,\n ): Promise<QuoteAuthorization | undefined> {\n const indexKey = this.botKey(\n identifier,\n \"quoteAuthorizationsByInteractingObject\",\n interactingObject.href,\n );\n return await this.withRedisLock(\n this.quoteAuthorizationLockKey(indexKey),\n async () => {\n const id = await this.get(indexKey);\n if (id == null) return undefined;\n const authorization = await this.getQuoteAuthorization(\n identifier,\n id as Uuid,\n );\n if (authorization == null && await this.get(indexKey) === id) {\n await this.del(\n indexKey,\n this.botKey(identifier, \"quoteAuthorizationInteractingObjects\", id),\n );\n }\n return authorization;\n },\n );\n }\n\n async removeQuoteAuthorization(\n identifier: string,\n id: Uuid,\n ): Promise<QuoteAuthorization | undefined> {\n const key = this.botKey(identifier, \"quoteAuthorizations\", id);\n const interactingObjectKey = this.botKey(\n identifier,\n \"quoteAuthorizationInteractingObjects\",\n id,\n );\n const storedInteractingObject = await this.get(interactingObjectKey) ??\n await this.findStoredQuoteAuthorizationInteractingObject(key);\n if (storedInteractingObject == null) return undefined;\n const indexKey = this.botKey(\n identifier,\n \"quoteAuthorizationsByInteractingObject\",\n storedInteractingObject,\n );\n const parsed = await this.withRedisLock(\n this.quoteAuthorizationLockKey(indexKey),\n async () => {\n const json = await this.get(key);\n if (json == null) {\n await this.del(interactingObjectKey);\n return undefined;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(json);\n } catch {\n return undefined;\n }\n const interactingObject = getRawQuoteAuthorizationInteractingObject(\n parsed,\n );\n if (interactingObject?.href !== storedInteractingObject) {\n return undefined;\n }\n await this.del(key, interactingObjectKey);\n if (await this.get(indexKey) === id) await this.del(indexKey);\n return parsed;\n },\n );\n if (parsed == null) return undefined;\n try {\n return await QuoteAuthorization.fromJsonLd(parsed);\n } catch {\n return undefined;\n }\n }\n\n private async findStoredQuoteAuthorizationInteractingObject(\n key: string,\n ): Promise<string | undefined> {\n const json = await this.get(key);\n if (json == null) return undefined;\n try {\n const interactingObject = getRawQuoteAuthorizationInteractingObject(\n JSON.parse(json),\n );\n return interactingObject?.href;\n } catch {\n return undefined;\n }\n }\n\n async addQuoteAuthorizationReference(\n identifier: string,\n authorization: URL,\n messageId: Uuid,\n attribution?: URL,\n ): Promise<void> {\n await this.withRedisLock(\n this.quoteAuthorizationReferenceLockKey(identifier, authorization),\n async () => {\n const value: QuoteAuthorizationReferenceData = attribution == null\n ? { messageId }\n : { messageId, attribution: attribution.href };\n await this.set(\n this.botKey(identifier, \"quoteAuthorizationRefs\", authorization.href),\n JSON.stringify(value),\n );\n await this.zAdd(\n this.key(\"index\", \"quoteAuthorizationRefs\", authorization.href),\n 0,\n identifier,\n );\n },\n );\n }\n\n async findQuoteAuthorizationReference(\n identifier: string,\n authorization: URL,\n ): Promise<Uuid | undefined> {\n const json = await this.get(\n this.botKey(identifier, \"quoteAuthorizationRefs\", authorization.href),\n );\n if (json == null) return undefined;\n try {\n const value = JSON.parse(json) as QuoteAuthorizationReferenceData;\n if (typeof value !== \"object\" || value == null) return undefined;\n return value.messageId;\n } catch {\n return undefined;\n }\n }\n\n async *findQuoteAuthorizationReferenceIdentifiers(\n authorization: URL,\n ): AsyncIterable<string> {\n yield* await this.zRange(\n this.key(\"index\", \"quoteAuthorizationRefs\", authorization.href),\n );\n }\n\n async findQuoteAuthorizationReferenceAttribution(\n identifier: string,\n authorization: URL,\n ): Promise<URL | undefined> {\n const json = await this.get(\n this.botKey(identifier, \"quoteAuthorizationRefs\", authorization.href),\n );\n if (json == null) return undefined;\n let value: QuoteAuthorizationReferenceData;\n try {\n value = JSON.parse(json) as QuoteAuthorizationReferenceData;\n } catch {\n return undefined;\n }\n if (typeof value !== \"object\" || value == null) return undefined;\n if (value.attribution == null) return undefined;\n try {\n return new URL(value.attribution);\n } catch {\n return undefined;\n }\n }\n\n async removeQuoteAuthorizationReference(\n identifier: string,\n authorization: URL,\n ): Promise<void> {\n await this.withRedisLock(\n this.quoteAuthorizationReferenceLockKey(identifier, authorization),\n async () => {\n await this.del(\n this.botKey(identifier, \"quoteAuthorizationRefs\", authorization.href),\n );\n await this.zRem(\n this.key(\"index\", \"quoteAuthorizationRefs\", authorization.href),\n identifier,\n );\n },\n );\n }\n\n async vote(\n identifier: string,\n messageId: Uuid,\n voterId: URL,\n option: string,\n ): Promise<void> {\n await this.sAdd(\n this.botKey(identifier, \"polls\", messageId, \"voters\"),\n voterId.href,\n );\n await this.sAdd(\n this.botKey(identifier, \"polls\", messageId, \"options\", option),\n voterId.href,\n );\n await this.zAdd(\n this.botKey(identifier, \"polls\", messageId, \"options\"),\n 0,\n option,\n );\n }\n\n countVoters(identifier: string, messageId: Uuid): Promise<number> {\n return this.sCard(this.botKey(identifier, \"polls\", messageId, \"voters\"));\n }\n\n async countVotes(\n identifier: string,\n messageId: Uuid,\n ): Promise<Readonly<Record<string, number>>> {\n const result: Record<string, number> = {};\n const options = await this.zRange(\n this.botKey(identifier, \"polls\", messageId, \"options\"),\n );\n await Promise.all(options.map(async (option) => {\n result[option] = await this.sCard(\n this.botKey(identifier, \"polls\", messageId, \"options\", option),\n );\n }));\n return result;\n }\n\n forIdentifier(identifier: string): ActorScopedRepository {\n return new ActorScopedRepository(this, identifier);\n }\n\n private quoteAuthorizationLockKey(indexKey: string): string {\n return `${indexKey}:lock`;\n }\n\n private quoteAuthorizationReferenceLockKey(\n identifier: string,\n authorization: URL,\n ): string {\n return this.botKey(\n identifier,\n \"quoteAuthorizationRefs\",\n authorization.href,\n \"lock\",\n );\n }\n}\n\nfunction toNumber(value: unknown): number {\n if (typeof value === \"number\") return value;\n if (typeof value === \"bigint\") return Number(value);\n if (typeof value === \"string\") return Number.parseInt(value, 10);\n return 0;\n}\n\nfunction validatePositiveMilliseconds(name: string, value: number): void {\n if (!Number.isFinite(value) || value <= 0) {\n throw new RangeError(`${name} must be a positive number.`);\n }\n}\n\nfunction toStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is string => typeof item === \"string\");\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction getMessageScore(id: Uuid, activity: Create | Announce): number {\n if (activity.published != null) return activity.published.epochMilliseconds;\n return extractTimestamp(id);\n}\n\nfunction extractTimestamp(uuid: string): number {\n const match = uuidV7Pattern.exec(uuid);\n if (match == null) return 0;\n return Number.parseInt(`${match[1]}${match[2]}`, 16);\n}\n\nfunction getRawQuoteAuthorizationInteractingObject(\n json: unknown,\n): URL | undefined {\n if (typeof json !== \"object\" || json == null) return undefined;\n if (!(\"interactingObject\" in json)) return undefined;\n const interactingObject = json.interactingObject;\n if (typeof interactingObject !== \"string\") return undefined;\n try {\n return new URL(interactingObject);\n } catch {\n return undefined;\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/botkit-redis",
3
- "version": "0.5.0-dev.0",
3
+ "version": "0.5.0-dev.329",
4
4
  "description": "Redis-based repository for BotKit",
5
5
  "license": "AGPL-3.0-only",
6
6
  "author": {
@@ -43,7 +43,7 @@
43
43
  "README.md"
44
44
  ],
45
45
  "peerDependencies": {
46
- "@fedify/botkit": "^0.5.0"
46
+ "@fedify/botkit": "^0.5.0-dev.329+223ad49e"
47
47
  },
48
48
  "dependencies": {
49
49
  "@fedify/fedify": "~2.3.1",
@@ -57,7 +57,7 @@
57
57
  },
58
58
  "scripts": {
59
59
  "build": "tsdown",
60
- "prepublish": "tsdown",
60
+ "prepublish": "mise run check && tsdown",
61
61
  "test": "tsdown && cd src/ && node --test --experimental-transform-types"
62
62
  }
63
63
  }