@fedify/botkit 0.5.0-dev.209 → 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.
- package/dist/bot-group.test.d.ts +2 -0
- package/dist/bot-group.test.js +220 -0
- package/dist/bot-group.test.js.map +1 -0
- package/dist/bot-impl.d.ts +132 -13
- package/dist/bot-impl.d.ts.map +1 -1
- package/dist/bot-impl.js +400 -178
- package/dist/bot-impl.js.map +1 -1
- package/dist/bot-impl.test.js +214 -76
- package/dist/bot-impl.test.js.map +1 -1
- package/dist/bot.d.ts +94 -48
- package/dist/bot.d.ts.map +1 -1
- package/dist/bot.js +2 -104
- package/dist/bot.js.map +1 -1
- package/dist/bot.test.js +59 -0
- package/dist/bot.test.js.map +1 -1
- package/dist/components/FollowButton.d.ts +5 -3
- package/dist/components/FollowButton.d.ts.map +1 -1
- package/dist/components/FollowButton.js +2 -2
- package/dist/components/FollowButton.js.map +1 -1
- package/dist/components/Follower.d.ts +2 -2
- package/dist/components/Layout.js +1 -1
- package/dist/components/Layout.js.map +1 -1
- package/dist/components/Message.d.ts +2 -2
- package/dist/deno.js +2 -1
- package/dist/deno.js.map +1 -1
- package/dist/follow-impl.test.js +3 -3
- package/dist/follow-impl.test.js.map +1 -1
- package/dist/instance-impl.d.ts +158 -0
- package/dist/instance-impl.d.ts.map +1 -0
- package/dist/instance-impl.js +603 -0
- package/dist/instance-impl.js.map +1 -0
- package/dist/instance-impl.test.d.ts +2 -0
- package/dist/instance-impl.test.js +103 -0
- package/dist/instance-impl.test.js.map +1 -0
- package/dist/instance-multi.test.d.ts +2 -0
- package/dist/instance-multi.test.js +151 -0
- package/dist/instance-multi.test.js.map +1 -0
- package/dist/instance-routing.test.d.ts +2 -0
- package/dist/instance-routing.test.js +367 -0
- package/dist/instance-routing.test.js.map +1 -0
- package/dist/instance.d.ts +318 -0
- package/dist/instance.d.ts.map +1 -0
- package/dist/instance.js +51 -0
- package/dist/instance.js.map +1 -0
- package/dist/message-impl.d.ts.map +1 -1
- package/dist/message-impl.js +17 -10
- package/dist/message-impl.js.map +1 -1
- package/dist/message-impl.test.js +43 -9
- package/dist/message-impl.test.js.map +1 -1
- package/dist/mod.d.ts +5 -3
- package/dist/mod.js +4 -2
- package/dist/pages.d.ts +10 -1
- package/dist/pages.d.ts.map +1 -1
- package/dist/pages.js +112 -41
- package/dist/pages.js.map +1 -1
- package/dist/pages.test.d.ts +2 -0
- package/dist/pages.test.js +170 -0
- package/dist/pages.test.js.map +1 -0
- package/dist/repository.d.ts +385 -138
- package/dist/repository.d.ts.map +1 -1
- package/dist/repository.js +595 -223
- package/dist/repository.js.map +1 -1
- package/dist/repository.test.js +564 -136
- package/dist/repository.test.js.map +1 -1
- package/dist/session-impl.d.ts.map +1 -1
- package/dist/session-impl.js +13 -4
- package/dist/session-impl.js.map +1 -1
- package/dist/session-impl.test.d.ts.map +1 -1
- package/dist/session-impl.test.js +9 -9
- package/dist/session-impl.test.js.map +1 -1
- package/dist/session.d.ts +8 -3
- package/dist/session.d.ts.map +1 -1
- package/dist/text.d.ts.map +1 -1
- package/dist/text.js +27 -10
- package/dist/text.js.map +1 -1
- package/dist/text.test.js +37 -2
- package/dist/text.test.js.map +1 -1
- package/dist/uri.d.ts +46 -0
- package/dist/uri.d.ts.map +1 -0
- package/dist/uri.js +64 -0
- package/dist/uri.js.map +1 -0
- package/dist/uri.test.d.ts +2 -0
- package/dist/uri.test.js +93 -0
- package/dist/uri.test.js.map +1 -0
- package/package.json +5 -1
package/dist/repository.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
239
|
+
* @param options The options for the repository.
|
|
21
240
|
*/
|
|
22
|
-
constructor(kv,
|
|
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.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
340
|
+
await this.kv.set(this.#key(identifier, "keyPairs"), pairs);
|
|
46
341
|
}
|
|
47
|
-
async getKeyPairs() {
|
|
48
|
-
const keyPairs = await this.kv.get(this
|
|
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 =
|
|
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 =
|
|
60
|
-
const listKey = this
|
|
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 =
|
|
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
|
|
83
|
-
const lockKey =
|
|
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 =
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
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 =
|
|
149
|
-
const listKey = this
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
175
|
-
const listKey = this
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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)
|
|
234
|
-
|
|
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(
|
|
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
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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
|
|
350
|
-
|
|
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
|
-
|
|
692
|
+
messages.set(id, newActivity);
|
|
354
693
|
return true;
|
|
355
694
|
}
|
|
356
|
-
removeMessage(id) {
|
|
357
|
-
const
|
|
358
|
-
|
|
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
|
|
380
|
-
|
|
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
|
|
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
|
|
387
|
-
const follower =
|
|
388
|
-
|
|
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
|
|
411
|
-
|
|
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
|
|
423
|
-
|
|
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
|
-
|
|
430
|
-
const
|
|
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`,
|
|
457
|
-
* operations like `countMessages` and `countFollowers
|
|
458
|
-
* always delegate to the
|
|
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
|
-
|
|
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
|
-
|
|
573
|
-
|
|
574
|
-
await this.underlying.vote(messageId, voterId, option);
|
|
926
|
+
findFollowedBots(followeeId) {
|
|
927
|
+
return this.underlying.findFollowedBots(followeeId);
|
|
575
928
|
}
|
|
576
|
-
async
|
|
577
|
-
|
|
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
|