@fedify/botkit 0.5.0-dev.210 → 0.5.0-dev.225

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.
Files changed (85) hide show
  1. package/dist/bot-group.test.d.ts +2 -0
  2. package/dist/bot-group.test.js +220 -0
  3. package/dist/bot-group.test.js.map +1 -0
  4. package/dist/bot-impl.d.ts +132 -13
  5. package/dist/bot-impl.d.ts.map +1 -1
  6. package/dist/bot-impl.js +400 -178
  7. package/dist/bot-impl.js.map +1 -1
  8. package/dist/bot-impl.test.js +214 -76
  9. package/dist/bot-impl.test.js.map +1 -1
  10. package/dist/bot.d.ts +94 -48
  11. package/dist/bot.d.ts.map +1 -1
  12. package/dist/bot.js +2 -104
  13. package/dist/bot.js.map +1 -1
  14. package/dist/bot.test.js +59 -0
  15. package/dist/bot.test.js.map +1 -1
  16. package/dist/components/FollowButton.d.ts +5 -3
  17. package/dist/components/FollowButton.d.ts.map +1 -1
  18. package/dist/components/FollowButton.js +2 -2
  19. package/dist/components/FollowButton.js.map +1 -1
  20. package/dist/components/Follower.d.ts +2 -2
  21. package/dist/components/Layout.js +1 -1
  22. package/dist/components/Layout.js.map +1 -1
  23. package/dist/components/Message.d.ts +2 -2
  24. package/dist/deno.js +2 -1
  25. package/dist/deno.js.map +1 -1
  26. package/dist/follow-impl.test.js +3 -3
  27. package/dist/follow-impl.test.js.map +1 -1
  28. package/dist/instance-impl.d.ts +158 -0
  29. package/dist/instance-impl.d.ts.map +1 -0
  30. package/dist/instance-impl.js +603 -0
  31. package/dist/instance-impl.js.map +1 -0
  32. package/dist/instance-impl.test.d.ts +2 -0
  33. package/dist/instance-impl.test.js +103 -0
  34. package/dist/instance-impl.test.js.map +1 -0
  35. package/dist/instance-multi.test.d.ts +2 -0
  36. package/dist/instance-multi.test.js +151 -0
  37. package/dist/instance-multi.test.js.map +1 -0
  38. package/dist/instance-routing.test.d.ts +2 -0
  39. package/dist/instance-routing.test.js +367 -0
  40. package/dist/instance-routing.test.js.map +1 -0
  41. package/dist/instance.d.ts +318 -0
  42. package/dist/instance.d.ts.map +1 -0
  43. package/dist/instance.js +51 -0
  44. package/dist/instance.js.map +1 -0
  45. package/dist/message-impl.d.ts.map +1 -1
  46. package/dist/message-impl.js +17 -10
  47. package/dist/message-impl.js.map +1 -1
  48. package/dist/message-impl.test.js +43 -9
  49. package/dist/message-impl.test.js.map +1 -1
  50. package/dist/mod.d.ts +5 -3
  51. package/dist/mod.js +4 -2
  52. package/dist/pages.d.ts +12 -3
  53. package/dist/pages.d.ts.map +1 -1
  54. package/dist/pages.js +112 -41
  55. package/dist/pages.js.map +1 -1
  56. package/dist/pages.test.d.ts +2 -0
  57. package/dist/pages.test.js +170 -0
  58. package/dist/pages.test.js.map +1 -0
  59. package/dist/repository.d.ts +385 -138
  60. package/dist/repository.d.ts.map +1 -1
  61. package/dist/repository.js +595 -223
  62. package/dist/repository.js.map +1 -1
  63. package/dist/repository.test.js +564 -136
  64. package/dist/repository.test.js.map +1 -1
  65. package/dist/session-impl.d.ts.map +1 -1
  66. package/dist/session-impl.js +13 -4
  67. package/dist/session-impl.js.map +1 -1
  68. package/dist/session-impl.test.d.ts.map +1 -1
  69. package/dist/session-impl.test.js +9 -9
  70. package/dist/session-impl.test.js.map +1 -1
  71. package/dist/session.d.ts +8 -3
  72. package/dist/session.d.ts.map +1 -1
  73. package/dist/text.d.ts.map +1 -1
  74. package/dist/text.js +27 -10
  75. package/dist/text.js.map +1 -1
  76. package/dist/text.test.js +37 -2
  77. package/dist/text.test.js.map +1 -1
  78. package/dist/uri.d.ts +46 -0
  79. package/dist/uri.d.ts.map +1 -0
  80. package/dist/uri.js +64 -0
  81. package/dist/uri.js.map +1 -0
  82. package/dist/uri.test.d.ts +2 -0
  83. package/dist/uri.test.js +93 -0
  84. package/dist/uri.test.js.map +1 -0
  85. package/package.json +5 -1
@@ -9,31 +9,326 @@ import { exportJwk, importJwk } from "@fedify/fedify/sig";
9
9
  //#region src/repository.ts
10
10
  const logger = getLogger(["botkit", "repository"]);
11
11
  /**
12
+ * A view of a {@link Repository} which is scoped to a single bot actor
13
+ * identifier. It exposes the same operations as {@link Repository} without
14
+ * the `identifier` parameter, which is bound at construction time.
15
+ * @since 0.5.0
16
+ */
17
+ var ActorScopedRepository = class {
18
+ /**
19
+ * The underlying repository.
20
+ */
21
+ repository;
22
+ /**
23
+ * The identifier of the bot actor this view is scoped to.
24
+ */
25
+ identifier;
26
+ /**
27
+ * Creates a new scoped repository view.
28
+ * @param repository The underlying repository.
29
+ * @param identifier The identifier of the bot actor to scope the view to.
30
+ */
31
+ constructor(repository, identifier) {
32
+ this.repository = repository;
33
+ this.identifier = identifier;
34
+ }
35
+ /**
36
+ * Sets the key pairs of the bot actor.
37
+ * @param keyPairs The key pairs to set.
38
+ */
39
+ setKeyPairs(keyPairs) {
40
+ return this.repository.setKeyPairs(this.identifier, keyPairs);
41
+ }
42
+ /**
43
+ * Gets the key pairs of the bot actor.
44
+ * @returns The key pairs of the bot actor. If the key pairs do not exist,
45
+ * `undefined` will be returned.
46
+ */
47
+ getKeyPairs() {
48
+ return this.repository.getKeyPairs(this.identifier);
49
+ }
50
+ /**
51
+ * Adds a message to the repository.
52
+ * @param id The UUID of the message.
53
+ * @param activity The activity to add.
54
+ */
55
+ addMessage(id, activity) {
56
+ return this.repository.addMessage(this.identifier, id, activity);
57
+ }
58
+ /**
59
+ * Updates a message in the repository.
60
+ * @param id The UUID of the message.
61
+ * @param updater The function to update the message. See also
62
+ * {@link Repository.updateMessage}.
63
+ * @returns `true` if the message was updated, `false` if the message does not
64
+ * exist.
65
+ */
66
+ updateMessage(id, updater) {
67
+ return this.repository.updateMessage(this.identifier, id, updater);
68
+ }
69
+ /**
70
+ * Removes a message from the repository.
71
+ * @param id The UUID of the message to remove.
72
+ * @returns The removed activity. If the message does not exist, `undefined`
73
+ * will be returned.
74
+ */
75
+ removeMessage(id) {
76
+ return this.repository.removeMessage(this.identifier, id);
77
+ }
78
+ /**
79
+ * Gets messages from the repository.
80
+ * @param options The options for getting messages.
81
+ * @returns An async iterable of message activities.
82
+ */
83
+ getMessages(options) {
84
+ return this.repository.getMessages(this.identifier, options);
85
+ }
86
+ /**
87
+ * Gets a message from the repository.
88
+ * @param id The UUID of the message to get.
89
+ * @returns The message activity, or `undefined` if the message does not
90
+ * exist.
91
+ */
92
+ getMessage(id) {
93
+ return this.repository.getMessage(this.identifier, id);
94
+ }
95
+ /**
96
+ * Counts the number of messages in the repository.
97
+ * @returns The number of messages in the repository.
98
+ */
99
+ countMessages() {
100
+ return this.repository.countMessages(this.identifier);
101
+ }
102
+ /**
103
+ * Adds a follower to the repository.
104
+ * @param followId The URL of the follow request.
105
+ * @param follower The actor who follows the bot.
106
+ */
107
+ addFollower(followId, follower) {
108
+ return this.repository.addFollower(this.identifier, followId, follower);
109
+ }
110
+ /**
111
+ * Removes a follower from the repository.
112
+ * @param followId The URL of the follow request.
113
+ * @param followerId The ID of the actor to remove.
114
+ * @returns The removed actor. If the follower does not exist or the follow
115
+ * request is not about the follower, `undefined` will be returned.
116
+ */
117
+ removeFollower(followId, followerId) {
118
+ return this.repository.removeFollower(this.identifier, followId, followerId);
119
+ }
120
+ /**
121
+ * Checks if the repository has a follower.
122
+ * @param followerId The ID of the follower to check.
123
+ * @returns `true` if the repository has the follower, `false` otherwise.
124
+ */
125
+ hasFollower(followerId) {
126
+ return this.repository.hasFollower(this.identifier, followerId);
127
+ }
128
+ /**
129
+ * Gets followers from the repository.
130
+ * @param options The options for getting followers.
131
+ * @returns An async iterable of actors who follow the bot.
132
+ */
133
+ getFollowers(options) {
134
+ return this.repository.getFollowers(this.identifier, options);
135
+ }
136
+ /**
137
+ * Counts the number of followers in the repository.
138
+ * @returns The number of followers in the repository.
139
+ */
140
+ countFollowers() {
141
+ return this.repository.countFollowers(this.identifier);
142
+ }
143
+ /**
144
+ * Adds a sent follow request to the repository.
145
+ * @param id The UUID of the follow request.
146
+ * @param follow The follow activity to add.
147
+ */
148
+ addSentFollow(id, follow) {
149
+ return this.repository.addSentFollow(this.identifier, id, follow);
150
+ }
151
+ /**
152
+ * Removes a sent follow request from the repository.
153
+ * @param id The UUID of the follow request to remove.
154
+ * @returns The removed follow activity. If the follow request does not
155
+ * exist, `undefined` will be returned.
156
+ */
157
+ removeSentFollow(id) {
158
+ return this.repository.removeSentFollow(this.identifier, id);
159
+ }
160
+ /**
161
+ * Gets a sent follow request from the repository.
162
+ * @param id The UUID of the follow request to get.
163
+ * @returns The `Follow` activity, or `undefined` if the follow request does
164
+ * not exist.
165
+ */
166
+ getSentFollow(id) {
167
+ return this.repository.getSentFollow(this.identifier, id);
168
+ }
169
+ /**
170
+ * Adds a followee to the repository.
171
+ * @param followeeId The ID of the followee to add.
172
+ * @param follow The follow activity to add.
173
+ */
174
+ addFollowee(followeeId, follow) {
175
+ return this.repository.addFollowee(this.identifier, followeeId, follow);
176
+ }
177
+ /**
178
+ * Removes a followee from the repository.
179
+ * @param followeeId The ID of the followee to remove.
180
+ * @returns The `Follow` activity that was removed. If the followee does not
181
+ * exist, `undefined` will be returned.
182
+ */
183
+ removeFollowee(followeeId) {
184
+ return this.repository.removeFollowee(this.identifier, followeeId);
185
+ }
186
+ /**
187
+ * Gets a followee from the repository.
188
+ * @param followeeId The ID of the followee to get.
189
+ * @returns The `Follow` activity, or `undefined` if the followee does not
190
+ * exist.
191
+ */
192
+ getFollowee(followeeId) {
193
+ return this.repository.getFollowee(this.identifier, followeeId);
194
+ }
195
+ /**
196
+ * Records a vote in a poll. If the same voter had already voted for the
197
+ * same option in a poll, the vote will be silently ignored.
198
+ * @param messageId The UUID of the poll message to vote on.
199
+ * @param voterId The ID of the voter.
200
+ * @param option The option that the voter is voting for.
201
+ */
202
+ vote(messageId, voterId, option) {
203
+ return this.repository.vote(this.identifier, messageId, voterId, option);
204
+ }
205
+ /**
206
+ * Counts the number of voters in a poll. Even if the poll allows multiple
207
+ * selections, each voter is counted only once.
208
+ * @param messageId The UUID of the poll message to count voters for.
209
+ * @returns The number of voters in the poll. If the poll does not exist,
210
+ * 0 will be returned.
211
+ */
212
+ countVoters(messageId) {
213
+ return this.repository.countVoters(this.identifier, messageId);
214
+ }
215
+ /**
216
+ * Counts the votes for each option in a poll.
217
+ * @param messageId The UUID of the poll message to count votes for.
218
+ * @returns A record where the keys are the options and the values are
219
+ * the number of votes for each option. See also
220
+ * {@link Repository.countVotes}.
221
+ */
222
+ countVotes(messageId) {
223
+ return this.repository.countVotes(this.identifier, messageId);
224
+ }
225
+ };
226
+ /**
12
227
  * A repository for storing bot data using a key-value store.
13
228
  */
14
229
  var KvRepository = class {
15
230
  kv;
16
- prefixes;
231
+ /**
232
+ * The key prefix under which all BotKit data is stored.
233
+ * @since 0.5.0
234
+ */
235
+ prefix;
17
236
  /**
18
237
  * Creates a new key-value store repository.
19
238
  * @param kv The key-value store to use.
20
- * @param prefixes The prefixes for key-value store keys.
239
+ * @param options The options for the repository.
21
240
  */
22
- constructor(kv, prefixes) {
241
+ constructor(kv, options = {}) {
23
242
  if (kv.cas == null) logger.warn("The given KvStore {kv} does not support CAS operations. This may cause issues with concurrent updates.", { kv });
24
243
  this.kv = kv;
25
- this.prefixes = {
26
- keyPairs: ["_botkit", "keyPairs"],
27
- messages: ["_botkit", "messages"],
28
- followers: ["_botkit", "followers"],
29
- followRequests: ["_botkit", "followRequests"],
30
- followees: ["_botkit", "followees"],
31
- follows: ["_botkit", "follows"],
32
- polls: ["_botkit", "polls"],
33
- ...prefixes ?? {}
34
- };
35
- }
36
- async setKeyPairs(keyPairs) {
244
+ this.prefix = options.prefix ?? ["_botkit"];
245
+ }
246
+ #key(identifier, ...rest) {
247
+ return [
248
+ ...this.prefix,
249
+ "bots",
250
+ identifier,
251
+ ...rest
252
+ ];
253
+ }
254
+ #followeeIndexKey(followeeId) {
255
+ return [
256
+ ...this.prefix,
257
+ "index",
258
+ "followees",
259
+ followeeId.href
260
+ ];
261
+ }
262
+ /**
263
+ * Migrates data stored by BotKit 0.4 or earlier, which was not scoped by
264
+ * bot actor identifiers, so that it belongs to the given identifier.
265
+ *
266
+ * The legacy data can be adopted by exactly one identifier. The adopter
267
+ * is claimed atomically (through a compare-and-set operation when the
268
+ * underlying store supports one) before anything is copied, so that
269
+ * reusing the repository for another bot, even concurrently, does not
270
+ * adopt the same rows again. Legacy keys are copied, not moved, and
271
+ * the completion is recorded last, so a partially failed run is simply
272
+ * retried by the adopter on the next call without data loss. Followees
273
+ * are also entered into the reverse lookup index used by
274
+ * {@link KvRepository.findFollowedBots}.
275
+ *
276
+ * Calling this method again after a successful migration is a no-op.
277
+ * @param identifier The identifier of the bot actor that adopts the legacy
278
+ * data.
279
+ * @since 0.5.0
280
+ */
281
+ async migrate(identifier) {
282
+ const markerKey = [...this.prefix, "migrated"];
283
+ let marker = await this.kv.get(markerKey);
284
+ if (marker == null) {
285
+ const claim = { adopter: identifier };
286
+ if (this.kv.cas == null) {
287
+ await this.kv.set(markerKey, claim);
288
+ marker = claim;
289
+ } else if (await this.kv.cas(markerKey, void 0, claim)) marker = claim;
290
+ else marker = await this.kv.get(markerKey);
291
+ }
292
+ if (marker == null || marker.adopter !== identifier || marker.done) return;
293
+ logger.info("Migrating legacy repository data to bot {identifier}...", { identifier });
294
+ const categories = [
295
+ "keyPairs",
296
+ "messages",
297
+ "followers",
298
+ "followRequests",
299
+ "followees",
300
+ "follows",
301
+ "polls"
302
+ ];
303
+ for (const category of categories) {
304
+ const legacyPrefix = [...this.prefix, category];
305
+ for await (const entry of this.kv.list(legacyPrefix)) {
306
+ const rest = entry.key.slice(this.prefix.length + 1);
307
+ if ((category === "messages" || category === "followers") && rest.length === 1 && rest[0] === "lock") continue;
308
+ const scopedKey = this.#key(identifier, category, ...rest);
309
+ if (await this.kv.get(scopedKey) == null) await this.kv.set(scopedKey, entry.value);
310
+ if (category === "followees" && rest.length === 1) {
311
+ let followeeId;
312
+ try {
313
+ followeeId = new URL(rest[0]);
314
+ } catch (error) {
315
+ logger.warn("Skipping the malformed legacy followee key {followeeId}.", {
316
+ followeeId: rest[0],
317
+ error
318
+ });
319
+ continue;
320
+ }
321
+ await this.#addToFolloweeIndex(identifier, followeeId);
322
+ }
323
+ }
324
+ }
325
+ await this.kv.set(markerKey, {
326
+ adopter: identifier,
327
+ done: true
328
+ });
329
+ logger.info("Finished migrating legacy repository data to bot {identifier}.", { identifier });
330
+ }
331
+ async setKeyPairs(identifier, keyPairs) {
37
332
  const pairs = [];
38
333
  for (const keyPair of keyPairs) {
39
334
  const pair = {
@@ -42,10 +337,10 @@ var KvRepository = class {
42
337
  };
43
338
  pairs.push(pair);
44
339
  }
45
- await this.kv.set(this.prefixes.keyPairs, pairs);
340
+ await this.kv.set(this.#key(identifier, "keyPairs"), pairs);
46
341
  }
47
- async getKeyPairs() {
48
- const keyPairs = await this.kv.get(this.prefixes.keyPairs);
342
+ async getKeyPairs(identifier) {
343
+ const keyPairs = await this.kv.get(this.#key(identifier, "keyPairs"));
49
344
  if (keyPairs == null) return void 0;
50
345
  const promises = keyPairs.map(async (pair) => ({
51
346
  privateKey: await importJwk(pair.private, "private"),
@@ -53,11 +348,11 @@ var KvRepository = class {
53
348
  }));
54
349
  return await Promise.all(promises);
55
350
  }
56
- async addMessage(id, activity) {
57
- const messageKey = [...this.prefixes.messages, id];
351
+ async addMessage(identifier, id, activity) {
352
+ const messageKey = this.#key(identifier, "messages", id);
58
353
  await this.kv.set(messageKey, await activity.toJsonLd({ format: "compact" }));
59
- const lockKey = [...this.prefixes.messages, "lock"];
60
- const listKey = this.prefixes.messages;
354
+ const lockKey = this.#key(identifier, "messages", "lock");
355
+ const listKey = this.#key(identifier, "messages");
61
356
  do {
62
357
  await this.kv.set(lockKey, id);
63
358
  const set = new Set(await this.kv.get(listKey) ?? []);
@@ -67,8 +362,8 @@ var KvRepository = class {
67
362
  await this.kv.set(listKey, list);
68
363
  } while (await this.kv.get(lockKey) !== id);
69
364
  }
70
- async updateMessage(id, updater) {
71
- const kvKey = [...this.prefixes.messages, id];
365
+ async updateMessage(identifier, id, updater) {
366
+ const kvKey = this.#key(identifier, "messages", id);
72
367
  const createJson = await this.kv.get(kvKey);
73
368
  if (createJson == null) return false;
74
369
  const activity = await Activity.fromJsonLd(createJson);
@@ -78,9 +373,9 @@ var KvRepository = class {
78
373
  await this.kv.set(kvKey, await newActivity.toJsonLd({ format: "compact" }));
79
374
  return true;
80
375
  }
81
- async removeMessage(id) {
82
- const listKey = this.prefixes.messages;
83
- const lockKey = [...listKey, "lock"];
376
+ async removeMessage(identifier, id) {
377
+ const listKey = this.#key(identifier, "messages");
378
+ const lockKey = this.#key(identifier, "messages", "lock");
84
379
  const lockId = `${id}:delete`;
85
380
  do {
86
381
  await this.kv.set(lockKey, lockId);
@@ -90,7 +385,7 @@ var KvRepository = class {
90
385
  list.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
91
386
  await this.kv.set(listKey, list);
92
387
  } while (await this.kv.get(lockKey) !== lockId);
93
- const messageKey = [...listKey, id];
388
+ const messageKey = this.#key(identifier, "messages", id);
94
389
  const activityJson = await this.kv.get(messageKey);
95
390
  if (activityJson == null) return;
96
391
  await this.kv.delete(messageKey);
@@ -98,11 +393,11 @@ var KvRepository = class {
98
393
  if (activity instanceof Create$1 || activity instanceof Announce$1) return activity;
99
394
  return void 0;
100
395
  }
101
- async *getMessages(options = {}) {
396
+ async *getMessages(identifier, options = {}) {
102
397
  const { order, until, since, limit } = options;
103
398
  const untilTs = until == null ? null : until.epochMilliseconds;
104
399
  const sinceTs = since == null ? null : since.epochMilliseconds;
105
- let messageIds = await this.kv.get(this.prefixes.messages) ?? [];
400
+ let messageIds = await this.kv.get(this.#key(identifier, "messages")) ?? [];
106
401
  if (sinceTs != null) {
107
402
  const offset = messageIds.findIndex((id) => extractTimestamp(id) >= sinceTs);
108
403
  messageIds = messageIds.slice(offset);
@@ -114,7 +409,7 @@ var KvRepository = class {
114
409
  if (order == null || order === "newest") messageIds = messageIds.toReversed();
115
410
  if (limit != null) messageIds = messageIds.slice(0, limit);
116
411
  for (const id of messageIds) {
117
- const messageJson = await this.kv.get([...this.prefixes.messages, id]);
412
+ const messageJson = await this.kv.get(this.#key(identifier, "messages", id));
118
413
  if (messageJson == null) continue;
119
414
  try {
120
415
  const activity = await Activity.fromJsonLd(messageJson);
@@ -124,8 +419,8 @@ var KvRepository = class {
124
419
  }
125
420
  }
126
421
  }
127
- async getMessage(id) {
128
- const json = await this.kv.get([...this.prefixes.messages, id]);
422
+ async getMessage(identifier, id) {
423
+ const json = await this.kv.get(this.#key(identifier, "messages", id));
129
424
  if (json == null) return void 0;
130
425
  let activity;
131
426
  try {
@@ -137,30 +432,30 @@ var KvRepository = class {
137
432
  if (activity instanceof Create$1 || activity instanceof Announce$1) return activity;
138
433
  return void 0;
139
434
  }
140
- async countMessages() {
141
- const messageIds = await this.kv.get(this.prefixes.messages) ?? [];
435
+ async countMessages(identifier) {
436
+ const messageIds = await this.kv.get(this.#key(identifier, "messages")) ?? [];
142
437
  return messageIds.length;
143
438
  }
144
- async addFollower(followRequestId, follower) {
439
+ async addFollower(identifier, followRequestId, follower) {
145
440
  if (follower.id == null) throw new TypeError("The follower ID is missing.");
146
- const followerKey = [...this.prefixes.followers, follower.id.href];
441
+ const followerKey = this.#key(identifier, "followers", follower.id.href);
147
442
  await this.kv.set(followerKey, await follower.toJsonLd({ format: "compact" }));
148
- const lockKey = [...this.prefixes.followers, "lock"];
149
- const listKey = this.prefixes.followers;
443
+ const lockKey = this.#key(identifier, "followers", "lock");
444
+ const listKey = this.#key(identifier, "followers");
150
445
  do {
151
446
  await this.kv.set(lockKey, follower.id.href);
152
447
  const list = await this.kv.get(listKey) ?? [];
153
448
  if (!list.includes(follower.id.href)) list.push(follower.id.href);
154
449
  await this.kv.set(listKey, list);
155
450
  } while (await this.kv.get(lockKey) !== follower.id.href);
156
- const followRequestKey = [...this.prefixes.followRequests, followRequestId.href];
451
+ const followRequestKey = this.#key(identifier, "followRequests", followRequestId.href);
157
452
  await this.kv.set(followRequestKey, follower.id.href);
158
453
  }
159
- async removeFollower(followRequestId, actorId) {
160
- const followRequestKey = [...this.prefixes.followRequests, followRequestId.href];
454
+ async removeFollower(identifier, followRequestId, actorId) {
455
+ const followRequestKey = this.#key(identifier, "followRequests", followRequestId.href);
161
456
  const followerId = await this.kv.get(followRequestKey);
162
457
  if (followerId == null) return void 0;
163
- const followerKey = [...this.prefixes.followers, followerId];
458
+ const followerKey = this.#key(identifier, "followers", followerId);
164
459
  if (followerId !== actorId.href) return void 0;
165
460
  const followerJson = await this.kv.get(followerKey);
166
461
  if (followerJson == null) return void 0;
@@ -171,8 +466,8 @@ var KvRepository = class {
171
466
  return void 0;
172
467
  }
173
468
  if (!isActor(follower)) return void 0;
174
- const lockKey = [...this.prefixes.followers, "lock"];
175
- const listKey = this.prefixes.followers;
469
+ const lockKey = this.#key(identifier, "followers", "lock");
470
+ const listKey = this.#key(identifier, "followers");
176
471
  do {
177
472
  await this.kv.set(lockKey, followerId);
178
473
  let list = await this.kv.get(listKey) ?? [];
@@ -183,16 +478,16 @@ var KvRepository = class {
183
478
  await this.kv.delete(followRequestKey);
184
479
  return follower;
185
480
  }
186
- async hasFollower(followerId) {
187
- return await this.kv.get([...this.prefixes.followers, followerId.href]) != null;
481
+ async hasFollower(identifier, followerId) {
482
+ return await this.kv.get(this.#key(identifier, "followers", followerId.href)) != null;
188
483
  }
189
- async *getFollowers(options = {}) {
484
+ async *getFollowers(identifier, options = {}) {
190
485
  const { offset = 0, limit } = options;
191
- let followerIds = await this.kv.get(this.prefixes.followers) ?? [];
486
+ let followerIds = await this.kv.get(this.#key(identifier, "followers")) ?? [];
192
487
  followerIds = followerIds.slice(offset);
193
488
  if (limit != null) followerIds = followerIds.slice(0, limit);
194
489
  for (const id of followerIds) {
195
- const json = await this.kv.get([...this.prefixes.followers, id]);
490
+ const json = await this.kv.get(this.#key(identifier, "followers", id));
196
491
  let actor;
197
492
  try {
198
493
  actor = await Object$1.fromJsonLd(json);
@@ -203,21 +498,21 @@ var KvRepository = class {
203
498
  if (isActor(actor)) yield actor;
204
499
  }
205
500
  }
206
- async countFollowers() {
207
- const followerIds = await this.kv.get(this.prefixes.followers) ?? [];
501
+ async countFollowers(identifier) {
502
+ const followerIds = await this.kv.get(this.#key(identifier, "followers")) ?? [];
208
503
  return followerIds.length;
209
504
  }
210
- async addSentFollow(id, follow) {
211
- await this.kv.set([...this.prefixes.follows, id], await follow.toJsonLd({ format: "compact" }));
505
+ async addSentFollow(identifier, id, follow) {
506
+ await this.kv.set(this.#key(identifier, "follows", id), await follow.toJsonLd({ format: "compact" }));
212
507
  }
213
- async removeSentFollow(id) {
214
- const follow = await this.getSentFollow(id);
508
+ async removeSentFollow(identifier, id) {
509
+ const follow = await this.getSentFollow(identifier, id);
215
510
  if (follow == null) return void 0;
216
- await this.kv.delete([...this.prefixes.follows, id]);
511
+ await this.kv.delete(this.#key(identifier, "follows", id));
217
512
  return follow;
218
513
  }
219
- async getSentFollow(id) {
220
- const followJson = await this.kv.get([...this.prefixes.follows, id]);
514
+ async getSentFollow(identifier, id) {
515
+ const followJson = await this.kv.get(this.#key(identifier, "follows", id));
221
516
  if (followJson == null) return void 0;
222
517
  try {
223
518
  return await Follow.fromJsonLd(followJson);
@@ -225,17 +520,22 @@ var KvRepository = class {
225
520
  return void 0;
226
521
  }
227
522
  }
228
- async addFollowee(followeeId, follow) {
229
- await this.kv.set([...this.prefixes.followees, followeeId.href], await follow.toJsonLd({ format: "compact" }));
523
+ async addFollowee(identifier, followeeId, follow) {
524
+ await this.kv.set(this.#key(identifier, "followees", followeeId.href), await follow.toJsonLd({ format: "compact" }));
525
+ await this.#addToFolloweeIndex(identifier, followeeId);
230
526
  }
231
- async removeFollowee(followeeId) {
232
- const follow = await this.getFollowee(followeeId);
233
- if (follow == null) return void 0;
234
- await this.kv.delete([...this.prefixes.followees, followeeId.href]);
527
+ async removeFollowee(identifier, followeeId) {
528
+ const follow = await this.getFollowee(identifier, followeeId);
529
+ if (follow == null) {
530
+ await this.#removeFromFolloweeIndex(identifier, followeeId);
531
+ return void 0;
532
+ }
533
+ await this.kv.delete(this.#key(identifier, "followees", followeeId.href));
534
+ await this.#removeFromFolloweeIndex(identifier, followeeId);
235
535
  return follow;
236
536
  }
237
- async getFollowee(followeeId) {
238
- const json = await this.kv.get([...this.prefixes.followees, followeeId.href]);
537
+ async getFollowee(identifier, followeeId) {
538
+ const json = await this.kv.get(this.#key(identifier, "followees", followeeId.href));
239
539
  if (json == null) return void 0;
240
540
  try {
241
541
  return await Follow.fromJsonLd(json);
@@ -243,18 +543,52 @@ var KvRepository = class {
243
543
  return void 0;
244
544
  }
245
545
  }
246
- async vote(messageId, voterId, option) {
247
- const key = [
248
- ...this.prefixes.polls,
249
- messageId,
250
- option
251
- ];
546
+ async *findFollowedBots(followeeId) {
547
+ const identifiers = await this.kv.get(this.#followeeIndexKey(followeeId)) ?? [];
548
+ for (const identifier of identifiers) yield identifier;
549
+ }
550
+ async #addToFolloweeIndex(identifier, followeeId) {
551
+ const key = this.#followeeIndexKey(followeeId);
552
+ while (true) {
553
+ const prev = await this.kv.get(key);
554
+ if (prev != null && prev.includes(identifier)) return;
555
+ const next = prev == null ? [identifier] : [...prev, identifier];
556
+ if (this.kv.cas == null) {
557
+ await this.kv.set(key, next);
558
+ return;
559
+ }
560
+ if (await this.kv.cas(key, prev, next)) return;
561
+ logger.trace("CAS operation failed, retrying to index followee {followeeId} for bot {identifier}.", {
562
+ followeeId: followeeId.href,
563
+ identifier
564
+ });
565
+ }
566
+ }
567
+ async #removeFromFolloweeIndex(identifier, followeeId) {
568
+ const key = this.#followeeIndexKey(followeeId);
569
+ while (true) {
570
+ const prev = await this.kv.get(key);
571
+ if (prev == null || !prev.includes(identifier)) return;
572
+ const next = prev.filter((id) => id !== identifier);
573
+ if (this.kv.cas == null) {
574
+ await this.kv.set(key, next);
575
+ return;
576
+ }
577
+ if (await this.kv.cas(key, prev, next)) return;
578
+ logger.trace("CAS operation failed, retrying to unindex followee {followeeId} for bot {identifier}.", {
579
+ followeeId: followeeId.href,
580
+ identifier
581
+ });
582
+ }
583
+ }
584
+ async vote(identifier, messageId, voterId, option) {
585
+ const key = this.#key(identifier, "polls", messageId, option);
252
586
  while (true) {
253
587
  const prev = await this.kv.get(key);
254
588
  if (prev != null && prev.includes(voterId.href)) return;
255
589
  const next = prev == null ? [voterId.href] : [...prev, voterId.href];
256
590
  if (this.kv.cas == null) {
257
- this.kv.set(key, next);
591
+ await this.kv.set(key, next);
258
592
  break;
259
593
  } else {
260
594
  const success = await this.kv.cas(key, prev, next);
@@ -266,13 +600,13 @@ var KvRepository = class {
266
600
  });
267
601
  }
268
602
  }
269
- const optionsKey = [...this.prefixes.polls, messageId];
603
+ const optionsKey = this.#key(identifier, "polls", messageId);
270
604
  while (true) {
271
605
  const prevOptions = await this.kv.get(optionsKey);
272
606
  if (prevOptions != null && prevOptions.includes(option)) return;
273
607
  const nextOptions = prevOptions == null ? [option] : [...prevOptions, option];
274
608
  if (this.kv.cas == null) {
275
- this.kv.set(optionsKey, nextOptions);
609
+ await this.kv.set(optionsKey, nextOptions);
276
610
  break;
277
611
  } else {
278
612
  const success = await this.kv.cas(optionsKey, prevOptions, nextOptions);
@@ -284,32 +618,27 @@ var KvRepository = class {
284
618
  }
285
619
  }
286
620
  }
287
- async countVoters(messageId) {
288
- const options = await this.kv.get([...this.prefixes.polls, messageId]) ?? [];
621
+ async countVoters(identifier, messageId) {
622
+ const options = await this.kv.get(this.#key(identifier, "polls", messageId)) ?? [];
289
623
  const result = /* @__PURE__ */ new Set();
290
624
  for (const option of options) {
291
- const voters = await this.kv.get([
292
- ...this.prefixes.polls,
293
- messageId,
294
- option
295
- ]);
625
+ const voters = await this.kv.get(this.#key(identifier, "polls", messageId, option));
296
626
  if (voters != null) for (const voter of voters) result.add(voter);
297
627
  }
298
628
  return result.size;
299
629
  }
300
- async countVotes(messageId) {
301
- const options = await this.kv.get([...this.prefixes.polls, messageId]) ?? [];
630
+ async countVotes(identifier, messageId) {
631
+ const options = await this.kv.get(this.#key(identifier, "polls", messageId)) ?? [];
302
632
  const result = {};
303
633
  for (const option of options) {
304
- const voters = await this.kv.get([
305
- ...this.prefixes.polls,
306
- messageId,
307
- option
308
- ]);
634
+ const voters = await this.kv.get(this.#key(identifier, "polls", messageId, option));
309
635
  result[option] = voters == null ? 0 : voters.length;
310
636
  }
311
637
  return result;
312
638
  }
639
+ forIdentifier(identifier) {
640
+ return new ActorScopedRepository(this, identifier);
641
+ }
313
642
  };
314
643
  /**
315
644
  * Extracts the timestamp from a UUIDv7.
@@ -327,125 +656,149 @@ function extractTimestamp(uuid) {
327
656
  * persistent and is only suitable for testing or development.
328
657
  */
329
658
  var MemoryRepository = class {
330
- keyPairs;
331
- messages = /* @__PURE__ */ new Map();
332
- followers = /* @__PURE__ */ new Map();
333
- followRequests = {};
334
- sentFollows = {};
335
- followees = {};
336
- polls = {};
337
- setKeyPairs(keyPairs) {
338
- this.keyPairs = keyPairs;
659
+ #data = /* @__PURE__ */ new Map();
660
+ #bucket(identifier) {
661
+ let data = this.#data.get(identifier);
662
+ if (data == null) {
663
+ data = {
664
+ messages: /* @__PURE__ */ new Map(),
665
+ followers: /* @__PURE__ */ new Map(),
666
+ followRequests: {},
667
+ sentFollows: {},
668
+ followees: {},
669
+ polls: {}
670
+ };
671
+ this.#data.set(identifier, data);
672
+ }
673
+ return data;
674
+ }
675
+ setKeyPairs(identifier, keyPairs) {
676
+ this.#bucket(identifier).keyPairs = keyPairs;
339
677
  return Promise.resolve();
340
678
  }
341
- getKeyPairs() {
342
- return Promise.resolve(this.keyPairs);
679
+ getKeyPairs(identifier) {
680
+ return Promise.resolve(this.#data.get(identifier)?.keyPairs);
343
681
  }
344
- addMessage(id, activity) {
345
- this.messages.set(id, activity);
682
+ addMessage(identifier, id, activity) {
683
+ this.#bucket(identifier).messages.set(id, activity);
346
684
  return Promise.resolve();
347
685
  }
348
- async updateMessage(id, updater) {
349
- const existing = this.messages.get(id);
350
- if (existing == null) return false;
686
+ async updateMessage(identifier, id, updater) {
687
+ const messages = this.#data.get(identifier)?.messages;
688
+ const existing = messages?.get(id);
689
+ if (messages == null || existing == null) return false;
351
690
  const newActivity = await updater(existing);
352
691
  if (newActivity == null) return false;
353
- this.messages.set(id, newActivity);
692
+ messages.set(id, newActivity);
354
693
  return true;
355
694
  }
356
- removeMessage(id) {
357
- const activity = this.messages.get(id);
358
- this.messages.delete(id);
695
+ removeMessage(identifier, id) {
696
+ const messages = this.#data.get(identifier)?.messages;
697
+ const activity = messages?.get(id);
698
+ messages?.delete(id);
359
699
  return Promise.resolve(activity);
360
700
  }
361
- async *getMessages(options = {}) {
701
+ async *getMessages(identifier, options = {}) {
362
702
  const { order, until, since, limit } = options;
363
- let messages = [...this.messages.values()];
703
+ let messages = [...this.#data.get(identifier)?.messages.values() ?? []];
364
704
  if (since != null) messages = messages.filter((message) => message.published != null && Temporal.Instant.compare(message.published, since) >= 0);
365
705
  if (until != null) messages = messages.filter((message) => message.published != null && Temporal.Instant.compare(message.published, until) <= 0);
366
706
  if (order === "oldest") messages.sort((a, b) => (a.published?.epochMilliseconds ?? 0) - (b.published?.epochMilliseconds ?? 0));
367
707
  else messages.sort((a, b) => (b.published?.epochMilliseconds ?? 0) - (a.published?.epochMilliseconds ?? 0));
368
- if (limit != null) messages.slice(0, limit);
708
+ if (limit != null) messages = messages.slice(0, limit);
369
709
  for (const message of messages) yield message;
370
710
  }
371
- getMessage(id) {
372
- return Promise.resolve(this.messages.get(id));
711
+ getMessage(identifier, id) {
712
+ return Promise.resolve(this.#data.get(identifier)?.messages.get(id));
373
713
  }
374
- countMessages() {
375
- return Promise.resolve(this.messages.size);
714
+ countMessages(identifier) {
715
+ return Promise.resolve(this.#data.get(identifier)?.messages.size ?? 0);
376
716
  }
377
- addFollower(followId, follower) {
717
+ addFollower(identifier, followId, follower) {
378
718
  if (follower.id == null) throw new TypeError("The follower ID is missing.");
379
- this.followers.set(follower.id.href, follower);
380
- this.followRequests[followId.href] = follower.id.href;
719
+ const data = this.#bucket(identifier);
720
+ data.followers.set(follower.id.href, follower);
721
+ data.followRequests[followId.href] = follower.id.href;
381
722
  return Promise.resolve();
382
723
  }
383
- removeFollower(followId, followerId) {
384
- const existing = this.followRequests[followId.href];
724
+ removeFollower(identifier, followId, followerId) {
725
+ const data = this.#data.get(identifier);
726
+ if (data == null) return Promise.resolve(void 0);
727
+ const existing = data.followRequests[followId.href];
385
728
  if (existing == null || existing !== followerId.href) return Promise.resolve(void 0);
386
- delete this.followRequests[followId.href];
387
- const follower = this.followers.get(followerId.href);
388
- this.followers.delete(followerId.href);
729
+ delete data.followRequests[followId.href];
730
+ const follower = data.followers.get(followerId.href);
731
+ data.followers.delete(followerId.href);
389
732
  return Promise.resolve(follower);
390
733
  }
391
- hasFollower(followerId) {
392
- return Promise.resolve(this.followers.has(followerId.href));
734
+ hasFollower(identifier, followerId) {
735
+ return Promise.resolve(this.#data.get(identifier)?.followers.has(followerId.href) ?? false);
393
736
  }
394
- async *getFollowers(options = {}) {
737
+ async *getFollowers(identifier, options = {}) {
395
738
  const { offset = 0, limit } = options;
396
- let followers = [...this.followers.values()];
739
+ let followers = [...this.#data.get(identifier)?.followers.values() ?? []];
397
740
  followers.sort((a, b) => b.id.href.localeCompare(a.id.href) ?? 0);
398
741
  if (offset > 0) followers = followers.slice(offset);
399
742
  if (limit != null) followers = followers.slice(0, limit);
400
743
  for (const follower of followers) yield follower;
401
744
  }
402
- countFollowers() {
403
- return Promise.resolve(this.followers.size);
745
+ countFollowers(identifier) {
746
+ return Promise.resolve(this.#data.get(identifier)?.followers.size ?? 0);
404
747
  }
405
- addSentFollow(id, follow) {
406
- this.sentFollows[id] = follow;
748
+ addSentFollow(identifier, id, follow) {
749
+ this.#bucket(identifier).sentFollows[id] = follow;
407
750
  return Promise.resolve();
408
751
  }
409
- removeSentFollow(id) {
410
- const follow = this.sentFollows[id];
411
- delete this.sentFollows[id];
752
+ removeSentFollow(identifier, id) {
753
+ const sentFollows = this.#data.get(identifier)?.sentFollows;
754
+ if (sentFollows == null) return Promise.resolve(void 0);
755
+ const follow = sentFollows[id];
756
+ delete sentFollows[id];
412
757
  return Promise.resolve(follow);
413
758
  }
414
- getSentFollow(id) {
415
- return Promise.resolve(this.sentFollows[id]);
759
+ getSentFollow(identifier, id) {
760
+ return Promise.resolve(this.#data.get(identifier)?.sentFollows[id]);
416
761
  }
417
- addFollowee(followeeId, follow) {
418
- this.followees[followeeId.href] = follow;
762
+ addFollowee(identifier, followeeId, follow) {
763
+ this.#bucket(identifier).followees[followeeId.href] = follow;
419
764
  return Promise.resolve();
420
765
  }
421
- removeFollowee(followeeId) {
422
- const follow = this.followees[followeeId.href];
423
- delete this.followees[followeeId.href];
766
+ removeFollowee(identifier, followeeId) {
767
+ const followees = this.#data.get(identifier)?.followees;
768
+ if (followees == null) return Promise.resolve(void 0);
769
+ const follow = followees[followeeId.href];
770
+ delete followees[followeeId.href];
424
771
  return Promise.resolve(follow);
425
772
  }
426
- getFollowee(followeeId) {
427
- return Promise.resolve(this.followees[followeeId.href]);
773
+ getFollowee(identifier, followeeId) {
774
+ return Promise.resolve(this.#data.get(identifier)?.followees[followeeId.href]);
428
775
  }
429
- vote(messageId, voterId, option) {
430
- const poll = this.polls[messageId] ??= {};
776
+ async *findFollowedBots(followeeId) {
777
+ for (const [identifier, data] of this.#data) if (followeeId.href in data.followees) yield identifier;
778
+ }
779
+ vote(identifier, messageId, voterId, option) {
780
+ const poll = this.#bucket(identifier).polls[messageId] ??= {};
431
781
  const voters = poll[option] ??= /* @__PURE__ */ new Set();
432
782
  voters.add(voterId.href);
433
783
  return Promise.resolve();
434
784
  }
435
- countVoters(messageId) {
436
- const poll = this.polls[messageId];
785
+ countVoters(identifier, messageId) {
786
+ const poll = this.#data.get(identifier)?.polls[messageId];
437
787
  if (poll == null) return Promise.resolve(0);
438
788
  let voters = /* @__PURE__ */ new Set();
439
789
  for (const votersSet of globalThis.Object.values(poll)) voters = voters.union(votersSet);
440
790
  return Promise.resolve(voters.size);
441
791
  }
442
- countVotes(messageId) {
443
- const poll = this.polls[messageId];
792
+ countVotes(identifier, messageId) {
793
+ const poll = this.#data.get(identifier)?.polls[messageId];
444
794
  if (poll == null) return Promise.resolve({});
445
795
  const counts = {};
446
796
  for (const [option, voters] of globalThis.Object.entries(poll)) counts[option] = voters.size;
447
797
  return Promise.resolve(counts);
448
798
  }
799
+ forIdentifier(identifier) {
800
+ return new ActorScopedRepository(this, identifier);
801
+ }
449
802
  };
450
803
  /**
451
804
  * A repository decorator that adds an in-memory cache layer on top of another
@@ -453,9 +806,10 @@ var MemoryRepository = class {
453
806
  * of accesses to the underlying persistent storage, but it increases memory
454
807
  * usage. The cache is not persistent and will be lost when the process exits.
455
808
  *
456
- * Note: List operations like `getMessages` and `getFollowers`, and count
457
- * operations like `countMessages` and `countFollowers` are not cached and
458
- * always delegate to the underlying repository.
809
+ * Note: List operations like `getMessages` and `getFollowers`, count
810
+ * operations like `countMessages` and `countFollowers`, and reverse lookups
811
+ * like `findFollowedBots` are not cached and always delegate to the
812
+ * underlying repository.
459
813
  * @since 0.3.0
460
814
  */
461
815
  var MemoryCachedRepository = class {
@@ -471,120 +825,138 @@ var MemoryCachedRepository = class {
471
825
  this.underlying = underlying;
472
826
  this.cache = cache ?? new MemoryRepository();
473
827
  }
474
- async setKeyPairs(keyPairs) {
475
- await this.underlying.setKeyPairs(keyPairs);
476
- await this.cache.setKeyPairs(keyPairs);
828
+ async setKeyPairs(identifier, keyPairs) {
829
+ await this.underlying.setKeyPairs(identifier, keyPairs);
830
+ await this.cache.setKeyPairs(identifier, keyPairs);
477
831
  }
478
- async getKeyPairs() {
479
- let keyPairs = await this.cache.getKeyPairs();
832
+ async getKeyPairs(identifier) {
833
+ let keyPairs = await this.cache.getKeyPairs(identifier);
480
834
  if (keyPairs === void 0) {
481
- keyPairs = await this.underlying.getKeyPairs();
482
- if (keyPairs !== void 0) await this.cache.setKeyPairs(keyPairs);
835
+ keyPairs = await this.underlying.getKeyPairs(identifier);
836
+ if (keyPairs !== void 0) await this.cache.setKeyPairs(identifier, keyPairs);
483
837
  }
484
838
  return keyPairs;
485
839
  }
486
- async addMessage(id, activity) {
487
- await this.underlying.addMessage(id, activity);
488
- await this.cache.addMessage(id, activity);
840
+ async addMessage(identifier, id, activity) {
841
+ await this.underlying.addMessage(identifier, id, activity);
842
+ await this.cache.addMessage(identifier, id, activity);
489
843
  }
490
- async updateMessage(id, updater) {
491
- const updated = await this.underlying.updateMessage(id, updater);
844
+ async updateMessage(identifier, id, updater) {
845
+ const updated = await this.underlying.updateMessage(identifier, id, updater);
492
846
  if (updated) {
493
- const updatedMessage = await this.underlying.getMessage(id);
494
- if (updatedMessage) await this.cache.addMessage(id, updatedMessage);
495
- else await this.cache.removeMessage(id);
847
+ const updatedMessage = await this.underlying.getMessage(identifier, id);
848
+ if (updatedMessage) await this.cache.addMessage(identifier, id, updatedMessage);
849
+ else await this.cache.removeMessage(identifier, id);
496
850
  }
497
851
  return updated;
498
852
  }
499
- async removeMessage(id) {
500
- const removedActivity = await this.underlying.removeMessage(id);
501
- if (removedActivity !== void 0) await this.cache.removeMessage(id);
853
+ async removeMessage(identifier, id) {
854
+ const removedActivity = await this.underlying.removeMessage(identifier, id);
855
+ if (removedActivity !== void 0) await this.cache.removeMessage(identifier, id);
502
856
  return removedActivity;
503
857
  }
504
- getMessages(options) {
505
- return this.underlying.getMessages(options);
858
+ getMessages(identifier, options) {
859
+ return this.underlying.getMessages(identifier, options);
506
860
  }
507
- async getMessage(id) {
508
- let message = await this.cache.getMessage(id);
861
+ async getMessage(identifier, id) {
862
+ let message = await this.cache.getMessage(identifier, id);
509
863
  if (message === void 0) {
510
- message = await this.underlying.getMessage(id);
511
- if (message !== void 0) await this.cache.addMessage(id, message);
864
+ message = await this.underlying.getMessage(identifier, id);
865
+ if (message !== void 0) await this.cache.addMessage(identifier, id, message);
512
866
  }
513
867
  return message;
514
868
  }
515
- countMessages() {
516
- return this.underlying.countMessages();
869
+ countMessages(identifier) {
870
+ return this.underlying.countMessages(identifier);
517
871
  }
518
- async addFollower(followId, follower) {
519
- await this.underlying.addFollower(followId, follower);
520
- await this.cache.addFollower(followId, follower);
872
+ async addFollower(identifier, followId, follower) {
873
+ await this.underlying.addFollower(identifier, followId, follower);
874
+ await this.cache.addFollower(identifier, followId, follower);
521
875
  }
522
- async removeFollower(followId, followerId) {
523
- const removedFollower = await this.underlying.removeFollower(followId, followerId);
524
- if (removedFollower !== void 0) await this.cache.removeFollower(followId, followerId);
876
+ async removeFollower(identifier, followId, followerId) {
877
+ const removedFollower = await this.underlying.removeFollower(identifier, followId, followerId);
878
+ if (removedFollower !== void 0) await this.cache.removeFollower(identifier, followId, followerId);
525
879
  return removedFollower;
526
880
  }
527
- async hasFollower(followerId) {
528
- if (await this.cache.hasFollower(followerId)) return true;
529
- const exists = await this.underlying.hasFollower(followerId);
881
+ async hasFollower(identifier, followerId) {
882
+ if (await this.cache.hasFollower(identifier, followerId)) return true;
883
+ const exists = await this.underlying.hasFollower(identifier, followerId);
530
884
  return exists;
531
885
  }
532
- getFollowers(options) {
533
- return this.underlying.getFollowers(options);
886
+ getFollowers(identifier, options) {
887
+ return this.underlying.getFollowers(identifier, options);
534
888
  }
535
- countFollowers() {
536
- return this.underlying.countFollowers();
889
+ countFollowers(identifier) {
890
+ return this.underlying.countFollowers(identifier);
537
891
  }
538
- async addSentFollow(id, follow) {
539
- await this.underlying.addSentFollow(id, follow);
540
- await this.cache.addSentFollow(id, follow);
892
+ async addSentFollow(identifier, id, follow) {
893
+ await this.underlying.addSentFollow(identifier, id, follow);
894
+ await this.cache.addSentFollow(identifier, id, follow);
541
895
  }
542
- async removeSentFollow(id) {
543
- const removedFollow = await this.underlying.removeSentFollow(id);
544
- if (removedFollow !== void 0) await this.cache.removeSentFollow(id);
896
+ async removeSentFollow(identifier, id) {
897
+ const removedFollow = await this.underlying.removeSentFollow(identifier, id);
898
+ if (removedFollow !== void 0) await this.cache.removeSentFollow(identifier, id);
545
899
  return removedFollow;
546
900
  }
547
- async getSentFollow(id) {
548
- let follow = await this.cache.getSentFollow(id);
901
+ async getSentFollow(identifier, id) {
902
+ let follow = await this.cache.getSentFollow(identifier, id);
549
903
  if (follow === void 0) {
550
- follow = await this.underlying.getSentFollow(id);
551
- if (follow !== void 0) await this.cache.addSentFollow(id, follow);
904
+ follow = await this.underlying.getSentFollow(identifier, id);
905
+ if (follow !== void 0) await this.cache.addSentFollow(identifier, id, follow);
552
906
  }
553
907
  return follow;
554
908
  }
555
- async addFollowee(followeeId, follow) {
556
- await this.underlying.addFollowee(followeeId, follow);
557
- await this.cache.addFollowee(followeeId, follow);
909
+ async addFollowee(identifier, followeeId, follow) {
910
+ await this.underlying.addFollowee(identifier, followeeId, follow);
911
+ await this.cache.addFollowee(identifier, followeeId, follow);
558
912
  }
559
- async removeFollowee(followeeId) {
560
- const removedFollow = await this.underlying.removeFollowee(followeeId);
561
- if (removedFollow !== void 0) await this.cache.removeFollowee(followeeId);
913
+ async removeFollowee(identifier, followeeId) {
914
+ const removedFollow = await this.underlying.removeFollowee(identifier, followeeId);
915
+ await this.cache.removeFollowee(identifier, followeeId);
562
916
  return removedFollow;
563
917
  }
564
- async getFollowee(followeeId) {
565
- let follow = await this.cache.getFollowee(followeeId);
918
+ async getFollowee(identifier, followeeId) {
919
+ let follow = await this.cache.getFollowee(identifier, followeeId);
566
920
  if (follow === void 0) {
567
- follow = await this.underlying.getFollowee(followeeId);
568
- if (follow !== void 0) await this.cache.addFollowee(followeeId, follow);
921
+ follow = await this.underlying.getFollowee(identifier, followeeId);
922
+ if (follow !== void 0) await this.cache.addFollowee(identifier, followeeId, follow);
569
923
  }
570
924
  return follow;
571
925
  }
572
- async vote(messageId, voterId, option) {
573
- await this.cache.vote(messageId, voterId, option);
574
- await this.underlying.vote(messageId, voterId, option);
926
+ findFollowedBots(followeeId) {
927
+ return this.underlying.findFollowedBots(followeeId);
575
928
  }
576
- async countVoters(messageId) {
577
- const voters = await this.cache.countVoters(messageId);
929
+ async vote(identifier, messageId, voterId, option) {
930
+ await this.cache.vote(identifier, messageId, voterId, option);
931
+ await this.underlying.vote(identifier, messageId, voterId, option);
932
+ }
933
+ async countVoters(identifier, messageId) {
934
+ const voters = await this.cache.countVoters(identifier, messageId);
578
935
  if (voters > 0) return voters;
579
- return this.underlying.countVoters(messageId);
936
+ return this.underlying.countVoters(identifier, messageId);
580
937
  }
581
- async countVotes(messageId) {
582
- const votes = await this.cache.countVotes(messageId);
938
+ async countVotes(identifier, messageId) {
939
+ const votes = await this.cache.countVotes(identifier, messageId);
583
940
  if (globalThis.Object.keys(votes).length > 0) return votes;
584
- return await this.underlying.countVotes(messageId);
941
+ return await this.underlying.countVotes(identifier, messageId);
942
+ }
943
+ forIdentifier(identifier) {
944
+ return new ActorScopedRepository(this, identifier);
945
+ }
946
+ /**
947
+ * Migrates data stored by BotKit 0.4 or earlier in the underlying
948
+ * repository, so that it belongs to the given identifier. The cache is
949
+ * not involved: it starts empty and only ever holds values read after
950
+ * the migration.
951
+ * @param identifier The identifier of the bot actor that adopts the
952
+ * legacy data.
953
+ * @since 0.5.0
954
+ */
955
+ async migrate(identifier) {
956
+ await this.underlying.migrate?.(identifier);
585
957
  }
586
958
  };
587
959
 
588
960
  //#endregion
589
- export { Announce, Create, KvRepository, MemoryCachedRepository, MemoryRepository };
961
+ export { ActorScopedRepository, Announce, Create, KvRepository, MemoryCachedRepository, MemoryRepository };
590
962
  //# sourceMappingURL=repository.js.map