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

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 (84) 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 +3 -1
  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/Layout.js +1 -1
  21. package/dist/components/Layout.js.map +1 -1
  22. package/dist/components/Message.d.ts +2 -2
  23. package/dist/deno.js +3 -13
  24. package/dist/deno.js.map +1 -1
  25. package/dist/follow-impl.test.js +3 -3
  26. package/dist/follow-impl.test.js.map +1 -1
  27. package/dist/instance-impl.d.ts +158 -0
  28. package/dist/instance-impl.d.ts.map +1 -0
  29. package/dist/instance-impl.js +603 -0
  30. package/dist/instance-impl.js.map +1 -0
  31. package/dist/instance-impl.test.d.ts +2 -0
  32. package/dist/instance-impl.test.js +103 -0
  33. package/dist/instance-impl.test.js.map +1 -0
  34. package/dist/instance-multi.test.d.ts +2 -0
  35. package/dist/instance-multi.test.js +151 -0
  36. package/dist/instance-multi.test.js.map +1 -0
  37. package/dist/instance-routing.test.d.ts +2 -0
  38. package/dist/instance-routing.test.js +367 -0
  39. package/dist/instance-routing.test.js.map +1 -0
  40. package/dist/instance.d.ts +318 -0
  41. package/dist/instance.d.ts.map +1 -0
  42. package/dist/instance.js +51 -0
  43. package/dist/instance.js.map +1 -0
  44. package/dist/message-impl.d.ts.map +1 -1
  45. package/dist/message-impl.js +17 -10
  46. package/dist/message-impl.js.map +1 -1
  47. package/dist/message-impl.test.js +43 -9
  48. package/dist/message-impl.test.js.map +1 -1
  49. package/dist/mod.d.ts +5 -3
  50. package/dist/mod.js +4 -2
  51. package/dist/pages.d.ts +10 -1
  52. package/dist/pages.d.ts.map +1 -1
  53. package/dist/pages.js +112 -41
  54. package/dist/pages.js.map +1 -1
  55. package/dist/pages.test.d.ts +2 -0
  56. package/dist/pages.test.js +170 -0
  57. package/dist/pages.test.js.map +1 -0
  58. package/dist/repository.d.ts +385 -138
  59. package/dist/repository.d.ts.map +1 -1
  60. package/dist/repository.js +595 -223
  61. package/dist/repository.js.map +1 -1
  62. package/dist/repository.test.js +564 -136
  63. package/dist/repository.test.js.map +1 -1
  64. package/dist/session-impl.d.ts.map +1 -1
  65. package/dist/session-impl.js +13 -4
  66. package/dist/session-impl.js.map +1 -1
  67. package/dist/session-impl.test.d.ts.map +1 -1
  68. package/dist/session-impl.test.js +9 -9
  69. package/dist/session-impl.test.js.map +1 -1
  70. package/dist/session.d.ts +8 -3
  71. package/dist/session.d.ts.map +1 -1
  72. package/dist/text.d.ts.map +1 -1
  73. package/dist/text.js +27 -10
  74. package/dist/text.js.map +1 -1
  75. package/dist/text.test.js +37 -2
  76. package/dist/text.test.js.map +1 -1
  77. package/dist/uri.d.ts +46 -0
  78. package/dist/uri.d.ts.map +1 -0
  79. package/dist/uri.js +64 -0
  80. package/dist/uri.js.map +1 -0
  81. package/dist/uri.test.d.ts +2 -0
  82. package/dist/uri.test.js +93 -0
  83. package/dist/uri.test.js.map +1 -0
  84. package/package.json +6 -2
@@ -7,7 +7,7 @@ import { MemoryKvStore } from "@fedify/fedify/federation";
7
7
  import { Create, Follow, Note, PUBLIC_COLLECTION, Person } from "@fedify/vocab";
8
8
  import assert from "node:assert";
9
9
  import { describe, test } from "node:test";
10
- import { importJwk } from "@fedify/fedify/sig";
10
+ import { exportJwk, importJwk } from "@fedify/fedify/sig";
11
11
 
12
12
  //#region src/repository.test.ts
13
13
  function createKvRepository() {
@@ -69,17 +69,17 @@ for (const name in factories) {
69
69
  describe(name, () => {
70
70
  const repo = factory();
71
71
  test("key pairs", async () => {
72
- assert.deepStrictEqual(await repo.getKeyPairs(), void 0);
73
- await repo.setKeyPairs(keyPairs);
74
- assert.deepStrictEqual(await repo.getKeyPairs(), keyPairs);
72
+ assert.deepStrictEqual(await repo.getKeyPairs("bot"), void 0);
73
+ await repo.setKeyPairs("bot", keyPairs);
74
+ assert.deepStrictEqual(await repo.getKeyPairs("bot"), keyPairs);
75
75
  });
76
76
  test("messages", async () => {
77
- assert.deepStrictEqual(await repo.countMessages(), 0);
78
- assert.deepStrictEqual(await repo.getMessage("01941f29-7c00-7fe8-ab0a-7b593990a3c0"), void 0);
79
- assert.deepStrictEqual(await repo.getMessage("0194244f-d800-7873-8993-ef71ccd47306"), void 0);
80
- assert.deepStrictEqual(await repo.getMessage("01942976-3400-7f34-872e-2cbf0f9eeac4"), void 0);
81
- assert.deepStrictEqual(await repo.getMessage("01942e9c-9000-7480-a553-7a6ce737ce14"), void 0);
82
- assert.deepStrictEqual(await Array.fromAsync(repo.getMessages()), []);
77
+ assert.deepStrictEqual(await repo.countMessages("bot"), 0);
78
+ assert.deepStrictEqual(await repo.getMessage("bot", "01941f29-7c00-7fe8-ab0a-7b593990a3c0"), void 0);
79
+ assert.deepStrictEqual(await repo.getMessage("bot", "0194244f-d800-7873-8993-ef71ccd47306"), void 0);
80
+ assert.deepStrictEqual(await repo.getMessage("bot", "01942976-3400-7f34-872e-2cbf0f9eeac4"), void 0);
81
+ assert.deepStrictEqual(await repo.getMessage("bot", "01942e9c-9000-7480-a553-7a6ce737ce14"), void 0);
82
+ assert.deepStrictEqual(await Array.fromAsync(repo.getMessages("bot")), []);
83
83
  const messageA = new Create({
84
84
  id: new URL("https://example.com/ap/create/01941f29-7c00-7fe8-ab0a-7b593990a3c0"),
85
85
  actor: new URL("https://example.com/ap/actor/bot"),
@@ -157,50 +157,50 @@ for (const name in factories) {
157
157
  published: Temporal.Instant.from("2025-01-03T00:00:00Z"),
158
158
  updated: Temporal.Instant.from("2025-01-03T12:00:00Z")
159
159
  });
160
- await repo.addMessage("01941f29-7c00-7fe8-ab0a-7b593990a3c0", messageA);
161
- assert.deepStrictEqual(await repo.countMessages(), 1);
162
- assert.deepStrictEqual(await (await repo.getMessage("01941f29-7c00-7fe8-ab0a-7b593990a3c0"))?.toJsonLd(), await messageA.toJsonLd());
163
- assert.deepStrictEqual(await repo.getMessage("0194244f-d800-7873-8993-ef71ccd47306"), void 0);
164
- assert.deepStrictEqual(await repo.getMessage("01942976-3400-7f34-872e-2cbf0f9eeac4"), void 0);
165
- assert.deepStrictEqual(await repo.getMessage("01942e9c-9000-7480-a553-7a6ce737ce14"), void 0);
166
- assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages())).map((m) => m.toJsonLd())), [await messageA.toJsonLd()]);
167
- await repo.addMessage("0194244f-d800-7873-8993-ef71ccd47306", messageB);
168
- assert.deepStrictEqual(await repo.countMessages(), 2);
169
- assert.deepStrictEqual(await (await repo.getMessage("01941f29-7c00-7fe8-ab0a-7b593990a3c0"))?.toJsonLd(), await messageA.toJsonLd());
170
- assert.deepStrictEqual(await (await repo.getMessage("0194244f-d800-7873-8993-ef71ccd47306"))?.toJsonLd(), await messageB.toJsonLd());
171
- assert.deepStrictEqual(await repo.getMessage("01942976-3400-7f34-872e-2cbf0f9eeac4"), void 0);
172
- assert.deepStrictEqual(await repo.getMessage("01942e9c-9000-7480-a553-7a6ce737ce14"), void 0);
173
- assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages())).map((m) => m.toJsonLd())), [await messageB.toJsonLd(), await messageA.toJsonLd()]);
174
- assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages({ order: "oldest" }))).map((m) => m.toJsonLd())), [await messageA.toJsonLd(), await messageB.toJsonLd()]);
175
- await repo.addMessage("01942976-3400-7f34-872e-2cbf0f9eeac4", messageC);
176
- assert.deepStrictEqual(await repo.countMessages(), 3);
177
- assert.deepStrictEqual(await (await repo.getMessage("01941f29-7c00-7fe8-ab0a-7b593990a3c0"))?.toJsonLd(), await messageA.toJsonLd());
178
- assert.deepStrictEqual(await (await repo.getMessage("0194244f-d800-7873-8993-ef71ccd47306"))?.toJsonLd(), await messageB.toJsonLd());
179
- assert.deepStrictEqual(await (await repo.getMessage("01942976-3400-7f34-872e-2cbf0f9eeac4"))?.toJsonLd(), await messageC.toJsonLd());
180
- assert.deepStrictEqual(await repo.getMessage("01942e9c-9000-7480-a553-7a6ce737ce14"), void 0);
181
- assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages({ order: "newest" }))).map((m) => m.toJsonLd())), [
160
+ await repo.addMessage("bot", "01941f29-7c00-7fe8-ab0a-7b593990a3c0", messageA);
161
+ assert.deepStrictEqual(await repo.countMessages("bot"), 1);
162
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01941f29-7c00-7fe8-ab0a-7b593990a3c0"))?.toJsonLd(), await messageA.toJsonLd());
163
+ assert.deepStrictEqual(await repo.getMessage("bot", "0194244f-d800-7873-8993-ef71ccd47306"), void 0);
164
+ assert.deepStrictEqual(await repo.getMessage("bot", "01942976-3400-7f34-872e-2cbf0f9eeac4"), void 0);
165
+ assert.deepStrictEqual(await repo.getMessage("bot", "01942e9c-9000-7480-a553-7a6ce737ce14"), void 0);
166
+ assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages("bot"))).map((m) => m.toJsonLd())), [await messageA.toJsonLd()]);
167
+ await repo.addMessage("bot", "0194244f-d800-7873-8993-ef71ccd47306", messageB);
168
+ assert.deepStrictEqual(await repo.countMessages("bot"), 2);
169
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01941f29-7c00-7fe8-ab0a-7b593990a3c0"))?.toJsonLd(), await messageA.toJsonLd());
170
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "0194244f-d800-7873-8993-ef71ccd47306"))?.toJsonLd(), await messageB.toJsonLd());
171
+ assert.deepStrictEqual(await repo.getMessage("bot", "01942976-3400-7f34-872e-2cbf0f9eeac4"), void 0);
172
+ assert.deepStrictEqual(await repo.getMessage("bot", "01942e9c-9000-7480-a553-7a6ce737ce14"), void 0);
173
+ assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages("bot"))).map((m) => m.toJsonLd())), [await messageB.toJsonLd(), await messageA.toJsonLd()]);
174
+ assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages("bot", { order: "oldest" }))).map((m) => m.toJsonLd())), [await messageA.toJsonLd(), await messageB.toJsonLd()]);
175
+ await repo.addMessage("bot", "01942976-3400-7f34-872e-2cbf0f9eeac4", messageC);
176
+ assert.deepStrictEqual(await repo.countMessages("bot"), 3);
177
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01941f29-7c00-7fe8-ab0a-7b593990a3c0"))?.toJsonLd(), await messageA.toJsonLd());
178
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "0194244f-d800-7873-8993-ef71ccd47306"))?.toJsonLd(), await messageB.toJsonLd());
179
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01942976-3400-7f34-872e-2cbf0f9eeac4"))?.toJsonLd(), await messageC.toJsonLd());
180
+ assert.deepStrictEqual(await repo.getMessage("bot", "01942e9c-9000-7480-a553-7a6ce737ce14"), void 0);
181
+ assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages("bot", { order: "newest" }))).map((m) => m.toJsonLd())), [
182
182
  await messageC.toJsonLd(),
183
183
  await messageB.toJsonLd(),
184
184
  await messageA.toJsonLd()
185
185
  ]);
186
- assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages({ order: "oldest" }))).map((m) => m.toJsonLd())), [
186
+ assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages("bot", { order: "oldest" }))).map((m) => m.toJsonLd())), [
187
187
  await messageA.toJsonLd(),
188
188
  await messageB.toJsonLd(),
189
189
  await messageC.toJsonLd()
190
190
  ]);
191
- await repo.addMessage("01942e9c-9000-7480-a553-7a6ce737ce14", messageD);
192
- assert.deepStrictEqual(await repo.countMessages(), 4);
193
- assert.deepStrictEqual(await (await repo.getMessage("01941f29-7c00-7fe8-ab0a-7b593990a3c0"))?.toJsonLd(), await messageA.toJsonLd());
194
- assert.deepStrictEqual(await (await repo.getMessage("0194244f-d800-7873-8993-ef71ccd47306"))?.toJsonLd(), await messageB.toJsonLd());
195
- assert.deepStrictEqual(await (await repo.getMessage("01942976-3400-7f34-872e-2cbf0f9eeac4"))?.toJsonLd(), await messageC.toJsonLd());
196
- assert.deepStrictEqual(await (await repo.getMessage("01942e9c-9000-7480-a553-7a6ce737ce14"))?.toJsonLd(), await messageD.toJsonLd());
197
- assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages())).map((m) => m.toJsonLd())), [
191
+ await repo.addMessage("bot", "01942e9c-9000-7480-a553-7a6ce737ce14", messageD);
192
+ assert.deepStrictEqual(await repo.countMessages("bot"), 4);
193
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01941f29-7c00-7fe8-ab0a-7b593990a3c0"))?.toJsonLd(), await messageA.toJsonLd());
194
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "0194244f-d800-7873-8993-ef71ccd47306"))?.toJsonLd(), await messageB.toJsonLd());
195
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01942976-3400-7f34-872e-2cbf0f9eeac4"))?.toJsonLd(), await messageC.toJsonLd());
196
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01942e9c-9000-7480-a553-7a6ce737ce14"))?.toJsonLd(), await messageD.toJsonLd());
197
+ assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages("bot"))).map((m) => m.toJsonLd())), [
198
198
  await messageD.toJsonLd(),
199
199
  await messageC.toJsonLd(),
200
200
  await messageB.toJsonLd(),
201
201
  await messageA.toJsonLd()
202
202
  ]);
203
- assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages({
203
+ assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages("bot", {
204
204
  order: "oldest",
205
205
  until: Temporal.Instant.from("2025-01-03T00:00:00Z")
206
206
  }))).map((m) => m.toJsonLd())), [
@@ -208,60 +208,60 @@ for (const name in factories) {
208
208
  await messageB.toJsonLd(),
209
209
  await messageC.toJsonLd()
210
210
  ]);
211
- assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages({ since: Temporal.Instant.from("2025-01-02T00:00:00Z") }))).map((m) => m.toJsonLd())), [
211
+ assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages("bot", { since: Temporal.Instant.from("2025-01-02T00:00:00Z") }))).map((m) => m.toJsonLd())), [
212
212
  await messageD.toJsonLd(),
213
213
  await messageC.toJsonLd(),
214
214
  await messageB.toJsonLd()
215
215
  ]);
216
- assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages({
216
+ assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages("bot", {
217
217
  until: Temporal.Instant.from("2025-01-03T00:00:00Z"),
218
218
  since: Temporal.Instant.from("2025-01-02T00:00:00Z")
219
219
  }))).map((m) => m.toJsonLd())), [await messageC.toJsonLd(), await messageB.toJsonLd()]);
220
- const removed = await repo.removeMessage("0194244f-d800-7873-8993-ef71ccd47306");
220
+ const removed = await repo.removeMessage("bot", "0194244f-d800-7873-8993-ef71ccd47306");
221
221
  assert.deepStrictEqual(await removed?.toJsonLd(), await messageB.toJsonLd());
222
- assert.deepStrictEqual(await repo.countMessages(), 3);
223
- assert.deepStrictEqual(await (await repo.getMessage("01941f29-7c00-7fe8-ab0a-7b593990a3c0"))?.toJsonLd(), await messageA.toJsonLd());
224
- assert.deepStrictEqual(await repo.getMessage("0194244f-d800-7873-8993-ef71ccd47306"), void 0);
225
- assert.deepStrictEqual(await (await repo.getMessage("01942976-3400-7f34-872e-2cbf0f9eeac4"))?.toJsonLd(), await messageC.toJsonLd());
226
- assert.deepStrictEqual(await (await repo.getMessage("01942e9c-9000-7480-a553-7a6ce737ce14"))?.toJsonLd(), await messageD.toJsonLd());
227
- assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages({ order: "newest" }))).map((m) => m.toJsonLd())), [
222
+ assert.deepStrictEqual(await repo.countMessages("bot"), 3);
223
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01941f29-7c00-7fe8-ab0a-7b593990a3c0"))?.toJsonLd(), await messageA.toJsonLd());
224
+ assert.deepStrictEqual(await repo.getMessage("bot", "0194244f-d800-7873-8993-ef71ccd47306"), void 0);
225
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01942976-3400-7f34-872e-2cbf0f9eeac4"))?.toJsonLd(), await messageC.toJsonLd());
226
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01942e9c-9000-7480-a553-7a6ce737ce14"))?.toJsonLd(), await messageD.toJsonLd());
227
+ assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages("bot", { order: "newest" }))).map((m) => m.toJsonLd())), [
228
228
  await messageD.toJsonLd(),
229
229
  await messageC.toJsonLd(),
230
230
  await messageA.toJsonLd()
231
231
  ]);
232
- assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages({ order: "oldest" }))).map((m) => m.toJsonLd())), [
232
+ assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getMessages("bot", { order: "oldest" }))).map((m) => m.toJsonLd())), [
233
233
  await messageA.toJsonLd(),
234
234
  await messageC.toJsonLd(),
235
235
  await messageD.toJsonLd()
236
236
  ]);
237
- await repo.updateMessage("01942976-3400-7f34-872e-2cbf0f9eeac4", async (messageC$1) => messageC$1.clone({
237
+ await repo.updateMessage("bot", "01942976-3400-7f34-872e-2cbf0f9eeac4", async (messageC$1) => messageC$1.clone({
238
238
  object: await messageC2.getObject(),
239
239
  updated: messageC2.updated
240
240
  }));
241
- assert.deepStrictEqual(await repo.countMessages(), 3);
242
- assert.deepStrictEqual(await (await repo.getMessage("01941f29-7c00-7fe8-ab0a-7b593990a3c0"))?.toJsonLd(), await messageA.toJsonLd());
243
- assert.deepStrictEqual(await repo.getMessage("0194244f-d800-7873-8993-ef71ccd47306"), void 0);
244
- assert.deepStrictEqual(await (await repo.getMessage("01942976-3400-7f34-872e-2cbf0f9eeac4"))?.toJsonLd(), await messageC2.toJsonLd());
245
- assert.deepStrictEqual(await (await repo.getMessage("01942e9c-9000-7480-a553-7a6ce737ce14"))?.toJsonLd(), await messageD.toJsonLd());
241
+ assert.deepStrictEqual(await repo.countMessages("bot"), 3);
242
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01941f29-7c00-7fe8-ab0a-7b593990a3c0"))?.toJsonLd(), await messageA.toJsonLd());
243
+ assert.deepStrictEqual(await repo.getMessage("bot", "0194244f-d800-7873-8993-ef71ccd47306"), void 0);
244
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01942976-3400-7f34-872e-2cbf0f9eeac4"))?.toJsonLd(), await messageC2.toJsonLd());
245
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01942e9c-9000-7480-a553-7a6ce737ce14"))?.toJsonLd(), await messageD.toJsonLd());
246
246
  let updaterCalled = false;
247
- const updated = await repo.updateMessage("00000000-0000-0000-0000-000000000000", (message) => {
247
+ const updated = await repo.updateMessage("bot", "00000000-0000-0000-0000-000000000000", (message) => {
248
248
  updaterCalled = true;
249
249
  return message;
250
250
  });
251
251
  assert.deepStrictEqual(updated, false);
252
252
  assert.deepStrictEqual(updaterCalled, false);
253
- assert.deepStrictEqual(await repo.countMessages(), 3);
254
- assert.deepStrictEqual(await (await repo.getMessage("01941f29-7c00-7fe8-ab0a-7b593990a3c0"))?.toJsonLd(), await messageA.toJsonLd());
255
- assert.deepStrictEqual(await repo.getMessage("0194244f-d800-7873-8993-ef71ccd47306"), void 0);
256
- assert.deepStrictEqual(await (await repo.getMessage("01942976-3400-7f34-872e-2cbf0f9eeac4"))?.toJsonLd(), await messageC2.toJsonLd());
257
- assert.deepStrictEqual(await (await repo.getMessage("01942e9c-9000-7480-a553-7a6ce737ce14"))?.toJsonLd(), await messageD.toJsonLd());
258
- const updated2 = await repo.updateMessage("01942e9c-9000-7480-a553-7a6ce737ce14", (_) => void 0);
253
+ assert.deepStrictEqual(await repo.countMessages("bot"), 3);
254
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01941f29-7c00-7fe8-ab0a-7b593990a3c0"))?.toJsonLd(), await messageA.toJsonLd());
255
+ assert.deepStrictEqual(await repo.getMessage("bot", "0194244f-d800-7873-8993-ef71ccd47306"), void 0);
256
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01942976-3400-7f34-872e-2cbf0f9eeac4"))?.toJsonLd(), await messageC2.toJsonLd());
257
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01942e9c-9000-7480-a553-7a6ce737ce14"))?.toJsonLd(), await messageD.toJsonLd());
258
+ const updated2 = await repo.updateMessage("bot", "01942e9c-9000-7480-a553-7a6ce737ce14", (_) => void 0);
259
259
  assert.deepStrictEqual(updated2, false);
260
- assert.deepStrictEqual(await repo.countMessages(), 3);
261
- assert.deepStrictEqual(await (await repo.getMessage("01941f29-7c00-7fe8-ab0a-7b593990a3c0"))?.toJsonLd(), await messageA.toJsonLd());
262
- assert.deepStrictEqual(await repo.getMessage("0194244f-d800-7873-8993-ef71ccd47306"), void 0);
263
- assert.deepStrictEqual(await (await repo.getMessage("01942976-3400-7f34-872e-2cbf0f9eeac4"))?.toJsonLd(), await messageC2.toJsonLd());
264
- assert.deepStrictEqual(await (await repo.getMessage("01942e9c-9000-7480-a553-7a6ce737ce14"))?.toJsonLd(), await messageD.toJsonLd());
260
+ assert.deepStrictEqual(await repo.countMessages("bot"), 3);
261
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01941f29-7c00-7fe8-ab0a-7b593990a3c0"))?.toJsonLd(), await messageA.toJsonLd());
262
+ assert.deepStrictEqual(await repo.getMessage("bot", "0194244f-d800-7873-8993-ef71ccd47306"), void 0);
263
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01942976-3400-7f34-872e-2cbf0f9eeac4"))?.toJsonLd(), await messageC2.toJsonLd());
264
+ assert.deepStrictEqual(await (await repo.getMessage("bot", "01942e9c-9000-7480-a553-7a6ce737ce14"))?.toJsonLd(), await messageD.toJsonLd());
265
265
  });
266
266
  test("followers", async () => {
267
267
  const followerA = new Person({
@@ -274,35 +274,35 @@ for (const name in factories) {
274
274
  preferredUsername: "jane"
275
275
  });
276
276
  const followFromB = new URL("https://example.com/ap/follow/8b76286d-5eef-4f02-8a16-080ff2b0e2ca");
277
- assert.deepStrictEqual(await repo.countFollowers(), 0);
278
- assert.deepStrictEqual(await repo.hasFollower(followerA.id), false);
279
- assert.deepStrictEqual(await repo.hasFollower(followerB.id), false);
280
- assert.deepStrictEqual(await Array.fromAsync(repo.getFollowers()), []);
281
- await repo.addFollower(followFromA, followerA);
282
- assert.deepStrictEqual(await repo.countFollowers(), 1);
283
- assert.ok(await repo.hasFollower(followerA.id));
284
- assert.deepStrictEqual(await repo.hasFollower(followerB.id), false);
285
- assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getFollowers())).map((f) => f.toJsonLd())), [await followerA.toJsonLd()]);
286
- await repo.addFollower(followFromB, followerB);
287
- assert.deepStrictEqual(await repo.countFollowers(), 2);
288
- assert.ok(await repo.hasFollower(followerA.id));
289
- assert.ok(await repo.hasFollower(followerB.id));
290
- assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getFollowers())).map((f) => f.toJsonLd())), [await followerA.toJsonLd(), await followerB.toJsonLd()]);
291
- assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getFollowers({ offset: 1 }))).map((f) => f.toJsonLd())), [await followerB.toJsonLd()]);
292
- assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getFollowers({ limit: 1 }))).map((f) => f.toJsonLd())), [await followerA.toJsonLd()]);
293
- assert.deepStrictEqual(await repo.removeFollower(followFromA, followerB.id), void 0);
294
- assert.deepStrictEqual(await repo.removeFollower(followFromB, followerA.id), void 0);
295
- assert.deepStrictEqual(await repo.countFollowers(), 2);
296
- assert.ok(await repo.hasFollower(followerA.id));
297
- assert.ok(await repo.hasFollower(followerB.id));
298
- await repo.removeFollower(followFromA, followerA.id);
299
- assert.deepStrictEqual(await repo.countFollowers(), 1);
300
- assert.deepStrictEqual(await repo.hasFollower(followerA.id), false);
301
- assert.ok(await repo.hasFollower(followerB.id));
302
- await repo.removeFollower(followFromB, followerB.id);
303
- assert.deepStrictEqual(await repo.countFollowers(), 0);
304
- assert.deepStrictEqual(await repo.hasFollower(followerA.id), false);
305
- assert.deepStrictEqual(await repo.hasFollower(followerB.id), false);
277
+ assert.deepStrictEqual(await repo.countFollowers("bot"), 0);
278
+ assert.deepStrictEqual(await repo.hasFollower("bot", followerA.id), false);
279
+ assert.deepStrictEqual(await repo.hasFollower("bot", followerB.id), false);
280
+ assert.deepStrictEqual(await Array.fromAsync(repo.getFollowers("bot")), []);
281
+ await repo.addFollower("bot", followFromA, followerA);
282
+ assert.deepStrictEqual(await repo.countFollowers("bot"), 1);
283
+ assert.ok(await repo.hasFollower("bot", followerA.id));
284
+ assert.deepStrictEqual(await repo.hasFollower("bot", followerB.id), false);
285
+ assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getFollowers("bot"))).map((f) => f.toJsonLd())), [await followerA.toJsonLd()]);
286
+ await repo.addFollower("bot", followFromB, followerB);
287
+ assert.deepStrictEqual(await repo.countFollowers("bot"), 2);
288
+ assert.ok(await repo.hasFollower("bot", followerA.id));
289
+ assert.ok(await repo.hasFollower("bot", followerB.id));
290
+ assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getFollowers("bot"))).map((f) => f.toJsonLd())), [await followerA.toJsonLd(), await followerB.toJsonLd()]);
291
+ assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getFollowers("bot", { offset: 1 }))).map((f) => f.toJsonLd())), [await followerB.toJsonLd()]);
292
+ assert.deepStrictEqual(await Promise.all((await Array.fromAsync(repo.getFollowers("bot", { limit: 1 }))).map((f) => f.toJsonLd())), [await followerA.toJsonLd()]);
293
+ assert.deepStrictEqual(await repo.removeFollower("bot", followFromA, followerB.id), void 0);
294
+ assert.deepStrictEqual(await repo.removeFollower("bot", followFromB, followerA.id), void 0);
295
+ assert.deepStrictEqual(await repo.countFollowers("bot"), 2);
296
+ assert.ok(await repo.hasFollower("bot", followerA.id));
297
+ assert.ok(await repo.hasFollower("bot", followerB.id));
298
+ await repo.removeFollower("bot", followFromA, followerA.id);
299
+ assert.deepStrictEqual(await repo.countFollowers("bot"), 1);
300
+ assert.deepStrictEqual(await repo.hasFollower("bot", followerA.id), false);
301
+ assert.ok(await repo.hasFollower("bot", followerB.id));
302
+ await repo.removeFollower("bot", followFromB, followerB.id);
303
+ assert.deepStrictEqual(await repo.countFollowers("bot"), 0);
304
+ assert.deepStrictEqual(await repo.hasFollower("bot", followerA.id), false);
305
+ assert.deepStrictEqual(await repo.hasFollower("bot", followerB.id), false);
306
306
  });
307
307
  test("sent follows", async () => {
308
308
  const follow = new Follow({
@@ -310,11 +310,11 @@ for (const name in factories) {
310
310
  actor: new URL("https://example.com/ap/actor/bot"),
311
311
  object: new URL("https://example.com/ap/actor/john")
312
312
  });
313
- assert.deepStrictEqual(await repo.getSentFollow("03a395a2-353a-4894-afdb-2cab31a7b004"), void 0);
314
- await repo.addSentFollow("03a395a2-353a-4894-afdb-2cab31a7b004", follow);
315
- assert.deepStrictEqual(await (await repo.getSentFollow("03a395a2-353a-4894-afdb-2cab31a7b004"))?.toJsonLd(), await follow.toJsonLd());
316
- await repo.removeSentFollow("03a395a2-353a-4894-afdb-2cab31a7b004");
317
- assert.deepStrictEqual(await repo.getSentFollow("03a395a2-353a-4894-afdb-2cab31a7b004"), void 0);
313
+ assert.deepStrictEqual(await repo.getSentFollow("bot", "03a395a2-353a-4894-afdb-2cab31a7b004"), void 0);
314
+ await repo.addSentFollow("bot", "03a395a2-353a-4894-afdb-2cab31a7b004", follow);
315
+ assert.deepStrictEqual(await (await repo.getSentFollow("bot", "03a395a2-353a-4894-afdb-2cab31a7b004"))?.toJsonLd(), await follow.toJsonLd());
316
+ await repo.removeSentFollow("bot", "03a395a2-353a-4894-afdb-2cab31a7b004");
317
+ assert.deepStrictEqual(await repo.getSentFollow("bot", "03a395a2-353a-4894-afdb-2cab31a7b004"), void 0);
318
318
  });
319
319
  test("followees", async () => {
320
320
  const followeeId = new URL("https://example.com/ap/actor/john");
@@ -323,11 +323,11 @@ for (const name in factories) {
323
323
  actor: new URL("https://example.com/ap/actor/bot"),
324
324
  object: followeeId
325
325
  });
326
- assert.deepStrictEqual(await repo.getFollowee(followeeId), void 0);
327
- await repo.addFollowee(followeeId, follow);
328
- assert.deepStrictEqual(await (await repo.getFollowee(followeeId))?.toJsonLd(), await follow.toJsonLd());
329
- await repo.removeFollowee(followeeId);
330
- assert.deepStrictEqual(await repo.getFollowee(followeeId), void 0);
326
+ assert.deepStrictEqual(await repo.getFollowee("bot", followeeId), void 0);
327
+ await repo.addFollowee("bot", followeeId, follow);
328
+ assert.deepStrictEqual(await (await repo.getFollowee("bot", followeeId))?.toJsonLd(), await follow.toJsonLd());
329
+ await repo.removeFollowee("bot", followeeId);
330
+ assert.deepStrictEqual(await repo.getFollowee("bot", followeeId), void 0);
331
331
  });
332
332
  test("poll voting", async () => {
333
333
  const messageId1 = "01945678-1234-7890-abcd-ef0123456789";
@@ -335,51 +335,51 @@ for (const name in factories) {
335
335
  const voter1 = new URL("https://example.com/ap/actor/alice");
336
336
  const voter2 = new URL("https://example.com/ap/actor/bob");
337
337
  const voter3 = new URL("https://example.com/ap/actor/charlie");
338
- assert.deepStrictEqual(await repo.countVoters(messageId1), 0);
339
- assert.deepStrictEqual(await repo.countVotes(messageId1), {});
340
- assert.deepStrictEqual(await repo.countVoters(messageId2), 0);
341
- assert.deepStrictEqual(await repo.countVotes(messageId2), {});
342
- await repo.vote(messageId1, voter1, "option1");
343
- assert.deepStrictEqual(await repo.countVoters(messageId1), 1);
344
- assert.deepStrictEqual(await repo.countVotes(messageId1), { "option1": 1 });
345
- await repo.vote(messageId1, voter1, "option1");
346
- assert.deepStrictEqual(await repo.countVoters(messageId1), 1);
347
- assert.deepStrictEqual(await repo.countVotes(messageId1), { "option1": 1 });
348
- await repo.vote(messageId1, voter1, "option2");
349
- assert.deepStrictEqual(await repo.countVoters(messageId1), 1);
350
- assert.deepStrictEqual(await repo.countVotes(messageId1), {
338
+ assert.deepStrictEqual(await repo.countVoters("bot", messageId1), 0);
339
+ assert.deepStrictEqual(await repo.countVotes("bot", messageId1), {});
340
+ assert.deepStrictEqual(await repo.countVoters("bot", messageId2), 0);
341
+ assert.deepStrictEqual(await repo.countVotes("bot", messageId2), {});
342
+ await repo.vote("bot", messageId1, voter1, "option1");
343
+ assert.deepStrictEqual(await repo.countVoters("bot", messageId1), 1);
344
+ assert.deepStrictEqual(await repo.countVotes("bot", messageId1), { "option1": 1 });
345
+ await repo.vote("bot", messageId1, voter1, "option1");
346
+ assert.deepStrictEqual(await repo.countVoters("bot", messageId1), 1);
347
+ assert.deepStrictEqual(await repo.countVotes("bot", messageId1), { "option1": 1 });
348
+ await repo.vote("bot", messageId1, voter1, "option2");
349
+ assert.deepStrictEqual(await repo.countVoters("bot", messageId1), 1);
350
+ assert.deepStrictEqual(await repo.countVotes("bot", messageId1), {
351
351
  "option1": 1,
352
352
  "option2": 1
353
353
  });
354
- await repo.vote(messageId1, voter2, "option1");
355
- assert.deepStrictEqual(await repo.countVoters(messageId1), 2);
356
- assert.deepStrictEqual(await repo.countVotes(messageId1), {
354
+ await repo.vote("bot", messageId1, voter2, "option1");
355
+ assert.deepStrictEqual(await repo.countVoters("bot", messageId1), 2);
356
+ assert.deepStrictEqual(await repo.countVotes("bot", messageId1), {
357
357
  "option1": 2,
358
358
  "option2": 1
359
359
  });
360
- await repo.vote(messageId1, voter3, "option3");
361
- assert.deepStrictEqual(await repo.countVoters(messageId1), 3);
362
- assert.deepStrictEqual(await repo.countVotes(messageId1), {
360
+ await repo.vote("bot", messageId1, voter3, "option3");
361
+ assert.deepStrictEqual(await repo.countVoters("bot", messageId1), 3);
362
+ assert.deepStrictEqual(await repo.countVotes("bot", messageId1), {
363
363
  "option1": 2,
364
364
  "option2": 1,
365
365
  "option3": 1
366
366
  });
367
- await repo.vote(messageId2, voter1, "optionA");
368
- await repo.vote(messageId2, voter2, "optionB");
369
- assert.deepStrictEqual(await repo.countVoters(messageId2), 2);
370
- assert.deepStrictEqual(await repo.countVotes(messageId2), {
367
+ await repo.vote("bot", messageId2, voter1, "optionA");
368
+ await repo.vote("bot", messageId2, voter2, "optionB");
369
+ assert.deepStrictEqual(await repo.countVoters("bot", messageId2), 2);
370
+ assert.deepStrictEqual(await repo.countVotes("bot", messageId2), {
371
371
  "optionA": 1,
372
372
  "optionB": 1
373
373
  });
374
- assert.deepStrictEqual(await repo.countVoters(messageId1), 3);
375
- assert.deepStrictEqual(await repo.countVotes(messageId1), {
374
+ assert.deepStrictEqual(await repo.countVoters("bot", messageId1), 3);
375
+ assert.deepStrictEqual(await repo.countVotes("bot", messageId1), {
376
376
  "option1": 2,
377
377
  "option2": 1,
378
378
  "option3": 1
379
379
  });
380
- await repo.vote(messageId1, voter1, "");
381
- assert.deepStrictEqual(await repo.countVoters(messageId1), 3);
382
- assert.deepStrictEqual(await repo.countVotes(messageId1), {
380
+ await repo.vote("bot", messageId1, voter1, "");
381
+ assert.deepStrictEqual(await repo.countVoters("bot", messageId1), 3);
382
+ assert.deepStrictEqual(await repo.countVotes("bot", messageId1), {
383
383
  "option1": 2,
384
384
  "option2": 1,
385
385
  "option3": 1,
@@ -388,6 +388,434 @@ for (const name in factories) {
388
388
  });
389
389
  });
390
390
  }
391
+ function createNote(uuid, actor) {
392
+ return new Create({
393
+ id: new URL(`https://example.com/ap/actor/${actor}/create/${uuid}`),
394
+ actor: new URL(`https://example.com/ap/actor/${actor}`),
395
+ to: new URL(`https://example.com/ap/actor/${actor}/followers`),
396
+ cc: PUBLIC_COLLECTION,
397
+ object: new Note({
398
+ id: new URL(`https://example.com/ap/actor/${actor}/note/${uuid}`),
399
+ attribution: new URL(`https://example.com/ap/actor/${actor}`),
400
+ to: new URL(`https://example.com/ap/actor/${actor}/followers`),
401
+ cc: PUBLIC_COLLECTION,
402
+ content: "Hello, world!",
403
+ published: Temporal.Instant.from("2025-01-01T00:00:00Z")
404
+ }),
405
+ published: Temporal.Instant.from("2025-01-01T00:00:00Z")
406
+ });
407
+ }
408
+ for (const name in factories) {
409
+ const factory = factories[name];
410
+ describe(`${name}: bot isolation`, () => {
411
+ test("key pairs are isolated by bot identifier", async () => {
412
+ const repo = factory();
413
+ await repo.setKeyPairs("botA", keyPairs);
414
+ assert.deepStrictEqual(await repo.getKeyPairs("botB"), void 0);
415
+ assert.deepStrictEqual(await repo.getKeyPairs("botA"), keyPairs);
416
+ });
417
+ test("messages are isolated by bot identifier", async () => {
418
+ const repo = factory();
419
+ const messageId = "01941f29-7c00-7fe8-ab0a-7b593990a3c0";
420
+ const message = createNote(messageId, "botA");
421
+ await repo.addMessage("botA", messageId, message);
422
+ assert.deepStrictEqual(await repo.getMessage("botB", messageId), void 0);
423
+ assert.deepStrictEqual(await repo.countMessages("botB"), 0);
424
+ assert.deepStrictEqual(await Array.fromAsync(repo.getMessages("botB")), []);
425
+ assert.deepStrictEqual(await repo.countMessages("botA"), 1);
426
+ assert.deepStrictEqual(await (await repo.getMessage("botA", messageId))?.toJsonLd(), await message.toJsonLd());
427
+ assert.deepStrictEqual(await repo.removeMessage("botB", messageId), void 0);
428
+ assert.deepStrictEqual(await repo.countMessages("botA"), 1);
429
+ let updaterCalled = false;
430
+ const updated = await repo.updateMessage("botB", messageId, (message$1) => {
431
+ updaterCalled = true;
432
+ return message$1;
433
+ });
434
+ assert.deepStrictEqual(updated, false);
435
+ assert.deepStrictEqual(updaterCalled, false);
436
+ });
437
+ test("followers are isolated by bot identifier", async () => {
438
+ const repo = factory();
439
+ const follower = new Person({
440
+ id: new URL("https://example.com/ap/actor/john"),
441
+ preferredUsername: "john"
442
+ });
443
+ const followId = new URL("https://example.com/ap/follow/be2da56a-0ea3-4a6a-9dff-2a1837be67e0");
444
+ await repo.addFollower("botA", followId, follower);
445
+ assert.deepStrictEqual(await repo.hasFollower("botB", follower.id), false);
446
+ assert.deepStrictEqual(await repo.countFollowers("botB"), 0);
447
+ assert.deepStrictEqual(await Array.fromAsync(repo.getFollowers("botB")), []);
448
+ assert.ok(await repo.hasFollower("botA", follower.id));
449
+ assert.deepStrictEqual(await repo.removeFollower("botB", followId, follower.id), void 0);
450
+ assert.ok(await repo.hasFollower("botA", follower.id));
451
+ });
452
+ test("sent follows are isolated by bot identifier", async () => {
453
+ const repo = factory();
454
+ const followUuid = "03a395a2-353a-4894-afdb-2cab31a7b004";
455
+ const follow = new Follow({
456
+ id: new URL(`https://example.com/ap/actor/botA/follow/${followUuid}`),
457
+ actor: new URL("https://example.com/ap/actor/botA"),
458
+ object: new URL("https://example.com/ap/actor/john")
459
+ });
460
+ await repo.addSentFollow("botA", followUuid, follow);
461
+ assert.deepStrictEqual(await repo.getSentFollow("botB", followUuid), void 0);
462
+ assert.deepStrictEqual(await repo.removeSentFollow("botB", followUuid), void 0);
463
+ assert.deepStrictEqual(await (await repo.getSentFollow("botA", followUuid))?.toJsonLd(), await follow.toJsonLd());
464
+ });
465
+ test("followees are isolated by bot identifier", async () => {
466
+ const repo = factory();
467
+ const followeeId = new URL("https://example.com/ap/actor/john");
468
+ const follow = new Follow({
469
+ id: new URL("https://example.com/ap/actor/botA/follow/03a395a2-353a-4894-afdb-2cab31a7b004"),
470
+ actor: new URL("https://example.com/ap/actor/botA"),
471
+ object: followeeId
472
+ });
473
+ await repo.addFollowee("botA", followeeId, follow);
474
+ assert.deepStrictEqual(await repo.getFollowee("botB", followeeId), void 0);
475
+ assert.deepStrictEqual(await repo.removeFollowee("botB", followeeId), void 0);
476
+ assert.deepStrictEqual(await (await repo.getFollowee("botA", followeeId))?.toJsonLd(), await follow.toJsonLd());
477
+ });
478
+ test("poll votes are isolated by bot identifier", async () => {
479
+ const repo = factory();
480
+ const messageId = "01945678-1234-7890-abcd-ef0123456789";
481
+ const voter = new URL("https://example.com/ap/actor/alice");
482
+ await repo.vote("botA", messageId, voter, "option1");
483
+ assert.deepStrictEqual(await repo.countVoters("botB", messageId), 0);
484
+ assert.deepStrictEqual(await repo.countVotes("botB", messageId), {});
485
+ assert.deepStrictEqual(await repo.countVoters("botA", messageId), 1);
486
+ assert.deepStrictEqual(await repo.countVotes("botA", messageId), { option1: 1 });
487
+ });
488
+ });
489
+ describe(`${name}: findFollowedBots()`, () => {
490
+ test("yields the identifiers of bots following the given actor", async () => {
491
+ const repo = factory();
492
+ const followeeId = new URL("https://example.com/ap/actor/john");
493
+ const otherFolloweeId = new URL("https://example.com/ap/actor/jane");
494
+ const followA = new Follow({
495
+ id: new URL("https://example.com/ap/actor/botA/follow/03a395a2-353a-4894-afdb-2cab31a7b004"),
496
+ actor: new URL("https://example.com/ap/actor/botA"),
497
+ object: followeeId
498
+ });
499
+ const followB = new Follow({
500
+ id: new URL("https://example.com/ap/actor/botB/follow/e35ff5d8-ede9-4f5e-9b83-4bfcd4c9a69c"),
501
+ actor: new URL("https://example.com/ap/actor/botB"),
502
+ object: followeeId
503
+ });
504
+ assert.deepStrictEqual(await Array.fromAsync(repo.findFollowedBots(followeeId)), []);
505
+ await repo.addFollowee("botA", followeeId, followA);
506
+ await repo.addFollowee("botB", followeeId, followB);
507
+ const bots = await Array.fromAsync(repo.findFollowedBots(followeeId));
508
+ bots.sort();
509
+ assert.deepStrictEqual(bots, ["botA", "botB"]);
510
+ assert.deepStrictEqual(await Array.fromAsync(repo.findFollowedBots(otherFolloweeId)), []);
511
+ await repo.addFollowee("botA", followeeId, followA);
512
+ const bots2 = await Array.fromAsync(repo.findFollowedBots(followeeId));
513
+ bots2.sort();
514
+ assert.deepStrictEqual(bots2, ["botA", "botB"]);
515
+ await repo.removeFollowee("botA", followeeId);
516
+ assert.deepStrictEqual(await Array.fromAsync(repo.findFollowedBots(followeeId)), ["botB"]);
517
+ await repo.removeFollowee("botB", followeeId);
518
+ assert.deepStrictEqual(await Array.fromAsync(repo.findFollowedBots(followeeId)), []);
519
+ });
520
+ });
521
+ describe(`${name}: forIdentifier()`, () => {
522
+ test("returns a repository view scoped to the given identifier", async () => {
523
+ const repo = factory();
524
+ const scoped = repo.forIdentifier("botA");
525
+ assert.deepStrictEqual(scoped.identifier, "botA");
526
+ const messageId = "01941f29-7c00-7fe8-ab0a-7b593990a3c0";
527
+ const message = createNote(messageId, "botA");
528
+ await scoped.addMessage(messageId, message);
529
+ assert.deepStrictEqual(await (await repo.getMessage("botA", messageId))?.toJsonLd(), await message.toJsonLd());
530
+ assert.deepStrictEqual(await repo.getMessage("botB", messageId), void 0);
531
+ assert.deepStrictEqual(await scoped.countMessages(), 1);
532
+ const follower = new Person({
533
+ id: new URL("https://example.com/ap/actor/john"),
534
+ preferredUsername: "john"
535
+ });
536
+ const followId = new URL("https://example.com/ap/follow/be2da56a-0ea3-4a6a-9dff-2a1837be67e0");
537
+ await repo.addFollower("botA", followId, follower);
538
+ assert.ok(await scoped.hasFollower(follower.id));
539
+ assert.deepStrictEqual(await scoped.countFollowers(), 1);
540
+ await scoped.setKeyPairs(keyPairs);
541
+ assert.deepStrictEqual(await scoped.getKeyPairs(), keyPairs);
542
+ assert.deepStrictEqual(await repo.getKeyPairs("botA"), keyPairs);
543
+ assert.deepStrictEqual(await repo.getKeyPairs("botB"), void 0);
544
+ });
545
+ });
546
+ }
547
+ describe("KvRepository.migrate()", () => {
548
+ async function seedLegacyData(kv) {
549
+ const messageId = "01941f29-7c00-7fe8-ab0a-7b593990a3c0";
550
+ const message = createNote(messageId, "bot");
551
+ const messageJson = await message.toJsonLd({ format: "compact" });
552
+ await kv.set(["_botkit", "messages"], [messageId]);
553
+ await kv.set([
554
+ "_botkit",
555
+ "messages",
556
+ messageId
557
+ ], messageJson);
558
+ const keyPairsData = [];
559
+ for (const pair of keyPairs) keyPairsData.push({
560
+ private: await exportJwk(pair.privateKey),
561
+ public: await exportJwk(pair.publicKey)
562
+ });
563
+ await kv.set(["_botkit", "keyPairs"], keyPairsData);
564
+ const follower = new Person({
565
+ id: new URL("https://example.com/ap/actor/john"),
566
+ preferredUsername: "john"
567
+ });
568
+ const followRequestId = new URL("https://example.com/ap/follow/be2da56a-0ea3-4a6a-9dff-2a1837be67e0");
569
+ await kv.set(["_botkit", "followers"], [follower.id.href]);
570
+ await kv.set([
571
+ "_botkit",
572
+ "followers",
573
+ follower.id.href
574
+ ], await follower.toJsonLd({ format: "compact" }));
575
+ await kv.set([
576
+ "_botkit",
577
+ "followRequests",
578
+ followRequestId.href
579
+ ], follower.id.href);
580
+ const followeeId = new URL("https://example.com/ap/actor/jane");
581
+ const followeeFollow = new Follow({
582
+ id: new URL("https://example.com/ap/follow/03a395a2-353a-4894-afdb-2cab31a7b004"),
583
+ actor: new URL("https://example.com/ap/actor/bot"),
584
+ object: followeeId
585
+ });
586
+ const followeeFollowJson = await followeeFollow.toJsonLd({ format: "compact" });
587
+ await kv.set([
588
+ "_botkit",
589
+ "followees",
590
+ followeeId.href
591
+ ], followeeFollowJson);
592
+ const sentFollowId = "e35ff5d8-ede9-4f5e-9b83-4bfcd4c9a69c";
593
+ const sentFollow = new Follow({
594
+ id: new URL(`https://example.com/ap/follow/${sentFollowId}`),
595
+ actor: new URL("https://example.com/ap/actor/bot"),
596
+ object: new URL("https://example.com/ap/actor/joe")
597
+ });
598
+ const sentFollowJson = await sentFollow.toJsonLd({ format: "compact" });
599
+ await kv.set([
600
+ "_botkit",
601
+ "follows",
602
+ sentFollowId
603
+ ], sentFollowJson);
604
+ await kv.set([
605
+ "_botkit",
606
+ "polls",
607
+ messageId
608
+ ], ["option1", "option2"]);
609
+ await kv.set([
610
+ "_botkit",
611
+ "polls",
612
+ messageId,
613
+ "option1"
614
+ ], ["https://example.com/ap/actor/alice", "https://example.com/ap/actor/bob"]);
615
+ await kv.set([
616
+ "_botkit",
617
+ "polls",
618
+ messageId,
619
+ "option2"
620
+ ], ["https://example.com/ap/actor/alice"]);
621
+ return {
622
+ messageId,
623
+ messageJson,
624
+ followerId: follower.id,
625
+ followRequestId,
626
+ followeeId,
627
+ followeeFollowJson,
628
+ sentFollowId,
629
+ sentFollowJson
630
+ };
631
+ }
632
+ test("adopts legacy unscoped data", async () => {
633
+ const kv = new MemoryKvStore();
634
+ const seed = await seedLegacyData(kv);
635
+ const repo = new KvRepository(kv);
636
+ await repo.migrate("bot");
637
+ assert.deepStrictEqual(await repo.getKeyPairs("bot"), keyPairs);
638
+ assert.deepStrictEqual(await repo.countMessages("bot"), 1);
639
+ assert.deepStrictEqual(await (await repo.getMessage("bot", seed.messageId))?.toJsonLd({ format: "compact" }), seed.messageJson);
640
+ assert.deepStrictEqual((await Array.fromAsync(repo.getMessages("bot"))).length, 1);
641
+ assert.ok(await repo.hasFollower("bot", seed.followerId));
642
+ assert.deepStrictEqual(await repo.countFollowers("bot"), 1);
643
+ assert.deepStrictEqual(await repo.countVoters("bot", seed.messageId), 2);
644
+ assert.deepStrictEqual(await repo.countVotes("bot", seed.messageId), {
645
+ option1: 2,
646
+ option2: 1
647
+ });
648
+ assert.deepStrictEqual(await repo.getKeyPairs("other"), void 0);
649
+ assert.deepStrictEqual(await repo.countMessages("other"), 0);
650
+ assert.ok(await kv.get([
651
+ "_botkit",
652
+ "messages",
653
+ seed.messageId
654
+ ]) != null);
655
+ });
656
+ test("is idempotent", async () => {
657
+ const kv = new MemoryKvStore();
658
+ const seed = await seedLegacyData(kv);
659
+ const repo = new KvRepository(kv);
660
+ await repo.migrate("bot");
661
+ await repo.migrate("bot");
662
+ assert.deepStrictEqual(await repo.countMessages("bot"), 1);
663
+ assert.deepStrictEqual(await repo.countFollowers("bot"), 1);
664
+ await repo.removeMessage("bot", seed.messageId);
665
+ await repo.migrate("bot");
666
+ assert.deepStrictEqual(await repo.countMessages("bot"), 0);
667
+ });
668
+ test("does nothing without legacy data", async () => {
669
+ const kv = new MemoryKvStore();
670
+ const repo = new KvRepository(kv);
671
+ await repo.migrate("bot");
672
+ assert.deepStrictEqual(await repo.countMessages("bot"), 0);
673
+ assert.deepStrictEqual(await repo.getKeyPairs("bot"), void 0);
674
+ });
675
+ test("does not clobber scoped data written before migration", async () => {
676
+ const kv = new MemoryKvStore();
677
+ await seedLegacyData(kv);
678
+ const repo = new KvRepository(kv);
679
+ await repo.setKeyPairs("bot", keyPairs.slice(0, 1));
680
+ await repo.migrate("bot");
681
+ assert.deepStrictEqual(await repo.getKeyPairs("bot"), [keyPairs[0]]);
682
+ });
683
+ test("migrates sent follows eagerly", async () => {
684
+ const kv = new MemoryKvStore();
685
+ const seed = await seedLegacyData(kv);
686
+ const repo = new KvRepository(kv);
687
+ await repo.migrate("bot");
688
+ const follow = await repo.getSentFollow("bot", seed.sentFollowId);
689
+ assert.deepStrictEqual(await follow?.toJsonLd({ format: "compact" }), seed.sentFollowJson);
690
+ assert.ok(await kv.get([
691
+ "_botkit",
692
+ "follows",
693
+ seed.sentFollowId
694
+ ]) != null);
695
+ const kv2 = new MemoryKvStore();
696
+ const seed2 = await seedLegacyData(kv2);
697
+ const repo2 = new KvRepository(kv2);
698
+ await repo2.migrate("bot");
699
+ assert.deepStrictEqual(await repo2.getSentFollow("other", seed2.sentFollowId), void 0);
700
+ });
701
+ test("migrates followees eagerly with their reverse index", async () => {
702
+ const kv = new MemoryKvStore();
703
+ const seed = await seedLegacyData(kv);
704
+ const repo = new KvRepository(kv);
705
+ await repo.migrate("bot");
706
+ assert.deepStrictEqual(await Array.fromAsync(repo.findFollowedBots(seed.followeeId)), ["bot"]);
707
+ const follow = await repo.getFollowee("bot", seed.followeeId);
708
+ assert.deepStrictEqual(await follow?.toJsonLd({ format: "compact" }), seed.followeeFollowJson);
709
+ });
710
+ test("migrates follow requests eagerly", async () => {
711
+ const kv = new MemoryKvStore();
712
+ const seed = await seedLegacyData(kv);
713
+ const repo = new KvRepository(kv);
714
+ await repo.migrate("bot");
715
+ const removed = await repo.removeFollower("bot", seed.followRequestId, seed.followerId);
716
+ assert.ok(removed != null);
717
+ assert.deepStrictEqual(await repo.hasFollower("bot", seed.followerId), false);
718
+ assert.deepStrictEqual(await repo.countFollowers("bot"), 0);
719
+ });
720
+ test("migrates poll options named like lock keys", async () => {
721
+ const kv = new MemoryKvStore();
722
+ const seed = await seedLegacyData(kv);
723
+ await kv.set([
724
+ "_botkit",
725
+ "polls",
726
+ seed.messageId
727
+ ], ["lock", "open"]);
728
+ await kv.set([
729
+ "_botkit",
730
+ "polls",
731
+ seed.messageId,
732
+ "lock"
733
+ ], ["https://example.com/ap/actor/voter1"]);
734
+ await kv.set([
735
+ "_botkit",
736
+ "polls",
737
+ seed.messageId,
738
+ "open"
739
+ ], ["https://example.com/ap/actor/voter2"]);
740
+ const repo = new KvRepository(kv);
741
+ await repo.migrate("bot");
742
+ assert.deepStrictEqual(await repo.countVotes("bot", seed.messageId), {
743
+ lock: 1,
744
+ open: 1
745
+ });
746
+ });
747
+ test("adopts legacy data for one identifier only", async () => {
748
+ const kv = new MemoryKvStore();
749
+ await seedLegacyData(kv);
750
+ const repo = new KvRepository(kv);
751
+ await repo.migrate("botA");
752
+ await repo.migrate("botB");
753
+ assert.deepStrictEqual(await repo.countMessages("botA"), 1);
754
+ assert.deepStrictEqual(await repo.countMessages("botB"), 0);
755
+ assert.deepStrictEqual(await repo.getKeyPairs("botB"), void 0);
756
+ assert.deepStrictEqual(await repo.countFollowers("botB"), 0);
757
+ });
758
+ test("claims the legacy data atomically under concurrency", async () => {
759
+ class SlowKvStore {
760
+ #inner = new MemoryKvStore();
761
+ async get(key) {
762
+ await new Promise((resolve) => setTimeout(resolve, 10));
763
+ return await this.#inner.get(key);
764
+ }
765
+ set(key, value) {
766
+ return this.#inner.set(key, value);
767
+ }
768
+ delete(key) {
769
+ return this.#inner.delete(key);
770
+ }
771
+ cas(key, expectedValue, newValue) {
772
+ return this.#inner.cas(key, expectedValue, newValue);
773
+ }
774
+ list(prefix) {
775
+ return this.#inner.list(prefix);
776
+ }
777
+ }
778
+ const kv = new SlowKvStore();
779
+ const messageId = "01941f29-7c00-7fe8-ab0a-7b593990a3c0";
780
+ const message = createNote(messageId, "bot");
781
+ await kv.set(["_botkit", "messages"], [messageId]);
782
+ await kv.set([
783
+ "_botkit",
784
+ "messages",
785
+ messageId
786
+ ], await message.toJsonLd({ format: "compact" }));
787
+ const repo = new KvRepository(kv);
788
+ await Promise.all([repo.migrate("botA"), repo.migrate("botB")]);
789
+ const counts = [await repo.countMessages("botA"), await repo.countMessages("botB")];
790
+ counts.sort();
791
+ assert.deepStrictEqual(counts, [0, 1]);
792
+ });
793
+ test("keeps the legacy data with the adopting identifier", async () => {
794
+ const kv = new MemoryKvStore();
795
+ const seed = await seedLegacyData(kv);
796
+ const repo = new KvRepository(kv);
797
+ await repo.migrate("botA");
798
+ await repo.migrate("botB");
799
+ assert.deepStrictEqual(await repo.getSentFollow("botB", seed.sentFollowId), void 0);
800
+ assert.ok(await repo.getSentFollow("botA", seed.sentFollowId) != null);
801
+ });
802
+ test("forwards migration through MemoryCachedRepository", async () => {
803
+ const kv = new MemoryKvStore();
804
+ const seed = await seedLegacyData(kv);
805
+ const repo = new MemoryCachedRepository(new KvRepository(kv));
806
+ await repo.migrate?.("bot");
807
+ assert.deepStrictEqual(await repo.getKeyPairs("bot"), keyPairs);
808
+ assert.deepStrictEqual(await repo.countMessages("bot"), 1);
809
+ assert.ok(await repo.hasFollower("bot", seed.followerId));
810
+ assert.deepStrictEqual(await (await repo.getSentFollow("bot", seed.sentFollowId))?.toJsonLd({ format: "compact" }), seed.sentFollowJson);
811
+ });
812
+ test("does not expose legacy data before migrate() is called", async () => {
813
+ const kv = new MemoryKvStore();
814
+ const seed = await seedLegacyData(kv);
815
+ const repo = new KvRepository(kv);
816
+ assert.deepStrictEqual(await repo.getSentFollow("bot", seed.sentFollowId), void 0);
817
+ });
818
+ });
391
819
 
392
820
  //#endregion
393
821
  //# sourceMappingURL=repository.test.js.map