@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.
Files changed (85) hide show
  1. package/dist/bot-group.test.d.ts +2 -0
  2. package/dist/bot-group.test.js +220 -0
  3. package/dist/bot-group.test.js.map +1 -0
  4. package/dist/bot-impl.d.ts +132 -13
  5. package/dist/bot-impl.d.ts.map +1 -1
  6. package/dist/bot-impl.js +400 -178
  7. package/dist/bot-impl.js.map +1 -1
  8. package/dist/bot-impl.test.js +214 -76
  9. package/dist/bot-impl.test.js.map +1 -1
  10. package/dist/bot.d.ts +94 -48
  11. package/dist/bot.d.ts.map +1 -1
  12. package/dist/bot.js +2 -104
  13. package/dist/bot.js.map +1 -1
  14. package/dist/bot.test.js +59 -0
  15. package/dist/bot.test.js.map +1 -1
  16. package/dist/components/FollowButton.d.ts +5 -3
  17. package/dist/components/FollowButton.d.ts.map +1 -1
  18. package/dist/components/FollowButton.js +2 -2
  19. package/dist/components/FollowButton.js.map +1 -1
  20. package/dist/components/Follower.d.ts +2 -2
  21. package/dist/components/Layout.js +1 -1
  22. package/dist/components/Layout.js.map +1 -1
  23. package/dist/components/Message.d.ts +2 -2
  24. package/dist/deno.js +2 -1
  25. package/dist/deno.js.map +1 -1
  26. package/dist/follow-impl.test.js +3 -3
  27. package/dist/follow-impl.test.js.map +1 -1
  28. package/dist/instance-impl.d.ts +158 -0
  29. package/dist/instance-impl.d.ts.map +1 -0
  30. package/dist/instance-impl.js +603 -0
  31. package/dist/instance-impl.js.map +1 -0
  32. package/dist/instance-impl.test.d.ts +2 -0
  33. package/dist/instance-impl.test.js +103 -0
  34. package/dist/instance-impl.test.js.map +1 -0
  35. package/dist/instance-multi.test.d.ts +2 -0
  36. package/dist/instance-multi.test.js +151 -0
  37. package/dist/instance-multi.test.js.map +1 -0
  38. package/dist/instance-routing.test.d.ts +2 -0
  39. package/dist/instance-routing.test.js +367 -0
  40. package/dist/instance-routing.test.js.map +1 -0
  41. package/dist/instance.d.ts +318 -0
  42. package/dist/instance.d.ts.map +1 -0
  43. package/dist/instance.js +51 -0
  44. package/dist/instance.js.map +1 -0
  45. package/dist/message-impl.d.ts.map +1 -1
  46. package/dist/message-impl.js +17 -10
  47. package/dist/message-impl.js.map +1 -1
  48. package/dist/message-impl.test.js +43 -9
  49. package/dist/message-impl.test.js.map +1 -1
  50. package/dist/mod.d.ts +5 -3
  51. package/dist/mod.js +4 -2
  52. package/dist/pages.d.ts +10 -1
  53. package/dist/pages.d.ts.map +1 -1
  54. package/dist/pages.js +112 -41
  55. package/dist/pages.js.map +1 -1
  56. package/dist/pages.test.d.ts +2 -0
  57. package/dist/pages.test.js +170 -0
  58. package/dist/pages.test.js.map +1 -0
  59. package/dist/repository.d.ts +385 -138
  60. package/dist/repository.d.ts.map +1 -1
  61. package/dist/repository.js +595 -223
  62. package/dist/repository.js.map +1 -1
  63. package/dist/repository.test.js +564 -136
  64. package/dist/repository.test.js.map +1 -1
  65. package/dist/session-impl.d.ts.map +1 -1
  66. package/dist/session-impl.js +13 -4
  67. package/dist/session-impl.js.map +1 -1
  68. package/dist/session-impl.test.d.ts.map +1 -1
  69. package/dist/session-impl.test.js +9 -9
  70. package/dist/session-impl.test.js.map +1 -1
  71. package/dist/session.d.ts +8 -3
  72. package/dist/session.d.ts.map +1 -1
  73. package/dist/text.d.ts.map +1 -1
  74. package/dist/text.js +27 -10
  75. package/dist/text.js.map +1 -1
  76. package/dist/text.test.js +37 -2
  77. package/dist/text.test.js.map +1 -1
  78. package/dist/uri.d.ts +46 -0
  79. package/dist/uri.d.ts.map +1 -0
  80. package/dist/uri.js +64 -0
  81. package/dist/uri.js.map +1 -0
  82. package/dist/uri.test.d.ts +2 -0
  83. package/dist/uri.test.js +93 -0
  84. package/dist/uri.test.js.map +1 -0
  85. package/package.json +5 -1
@@ -0,0 +1,367 @@
1
+
2
+ import { Temporal, toTemporalInstant } from "@js-temporal/polyfill";
3
+ Date.prototype.toTemporalInstant = toTemporalInstant;
4
+
5
+ import { MemoryRepository } from "./repository.js";
6
+ import { BotImpl } from "./bot-impl.js";
7
+ import { InstanceImpl } from "./instance-impl.js";
8
+ import { MemoryKvStore } from "@fedify/fedify/federation";
9
+ import { Accept, Create, Follow, Like, Mention, Note, PUBLIC_COLLECTION, Person, Undo } from "@fedify/vocab";
10
+ import assert from "node:assert";
11
+ import { describe, test } from "node:test";
12
+
13
+ //#region src/instance-routing.test.ts
14
+ function createMockInboxContext(instance, origin, contextData, recipient) {
15
+ const ctx = instance.federation.createContext(new URL(origin), contextData);
16
+ Object.defineProperty(ctx, "recipient", {
17
+ value: recipient ?? null,
18
+ writable: true,
19
+ configurable: true
20
+ });
21
+ Object.defineProperty(ctx, "sendActivity", {
22
+ value: () => Promise.resolve(),
23
+ writable: true,
24
+ configurable: true
25
+ });
26
+ Object.defineProperty(ctx, "forwardActivity", {
27
+ value: () => Promise.resolve(),
28
+ writable: true,
29
+ configurable: true
30
+ });
31
+ return ctx;
32
+ }
33
+ function createHarness() {
34
+ const repository = new MemoryRepository();
35
+ const instance = new InstanceImpl({
36
+ kv: new MemoryKvStore(),
37
+ repository
38
+ });
39
+ const alpha = instance.createBot("alpha", { username: "alphabot" });
40
+ const beta = instance.createBot("beta", { username: "betabot" });
41
+ const ctx = createMockInboxContext(instance, "https://example.com/", void 0);
42
+ return {
43
+ instance,
44
+ repository,
45
+ alpha,
46
+ beta,
47
+ ctx
48
+ };
49
+ }
50
+ function remotePerson(handle) {
51
+ return new Person({
52
+ id: new URL(`https://example.com/ap/actor/${handle}`),
53
+ preferredUsername: handle
54
+ });
55
+ }
56
+ describe("shared inbox routing", () => {
57
+ test("routes Follow to the followed bot only", async () => {
58
+ const { instance, alpha, beta, ctx } = createHarness();
59
+ const followed = [];
60
+ alpha.onFollow = (session) => void followed.push(session.bot.identifier);
61
+ beta.onFollow = (session) => void followed.push(session.bot.identifier);
62
+ await instance.onFollowed(ctx, new Follow({
63
+ id: new URL("https://remote.example/follows/1"),
64
+ actor: remotePerson("john"),
65
+ object: new URL("https://example.com/ap/actor/alpha")
66
+ }));
67
+ assert.deepStrictEqual(followed, ["alpha"]);
68
+ });
69
+ test("routes Accept to the bot that sent the follow", async () => {
70
+ const { instance, repository, alpha, beta, ctx } = createHarness();
71
+ const accepted = [];
72
+ alpha.onAcceptFollow = (session) => void accepted.push(session.bot.identifier);
73
+ beta.onAcceptFollow = (session) => void accepted.push(session.bot.identifier);
74
+ const followId = "9d952a10-77e6-46bd-a48a-208b47e5e2bb";
75
+ await repository.addSentFollow("alpha", followId, new Follow({
76
+ id: new URL(`https://example.com/ap/actor/alpha/follow/${followId}`),
77
+ actor: new URL("https://example.com/ap/actor/alpha"),
78
+ object: remotePerson("john")
79
+ }));
80
+ await instance.onFollowAccepted(ctx, new Accept({
81
+ actor: new URL("https://example.com/ap/actor/john"),
82
+ object: new URL(`https://example.com/ap/actor/alpha/follow/${followId}`)
83
+ }));
84
+ assert.deepStrictEqual(accepted, ["alpha"]);
85
+ });
86
+ test("routes Like to the bot owning the message", async () => {
87
+ const { instance, repository, alpha, beta, ctx } = createHarness();
88
+ const liked = [];
89
+ alpha.onLike = (session) => void liked.push(session.bot.identifier);
90
+ beta.onLike = (session) => void liked.push(session.bot.identifier);
91
+ const messageId = "01941f29-7c00-7fe8-ab0a-7b593990a3c0";
92
+ await repository.addMessage("alpha", messageId, new Create({
93
+ id: new URL(`https://example.com/ap/actor/alpha/create/${messageId}`),
94
+ actor: new URL("https://example.com/ap/actor/alpha"),
95
+ to: PUBLIC_COLLECTION,
96
+ object: new Note({
97
+ id: new URL(`https://example.com/ap/actor/alpha/note/${messageId}`),
98
+ attribution: new URL("https://example.com/ap/actor/alpha"),
99
+ to: PUBLIC_COLLECTION,
100
+ content: "Hello!"
101
+ })
102
+ }));
103
+ await instance.onLiked(ctx, new Like({
104
+ id: new URL("https://remote.example/likes/1"),
105
+ actor: remotePerson("john"),
106
+ object: new URL(`https://example.com/ap/actor/alpha/note/${messageId}`)
107
+ }));
108
+ assert.deepStrictEqual(liked, ["alpha"]);
109
+ });
110
+ test("drops Like on objects the instance does not own", async () => {
111
+ const { instance, alpha, beta, ctx } = createHarness();
112
+ const liked = [];
113
+ alpha.onLike = (session) => void liked.push(session.bot.identifier);
114
+ beta.onLike = (session) => void liked.push(session.bot.identifier);
115
+ await instance.onLiked(ctx, new Like({
116
+ id: new URL("https://remote.example/likes/2"),
117
+ actor: remotePerson("john"),
118
+ object: new URL("https://remote.example/notes/1")
119
+ }));
120
+ assert.deepStrictEqual(liked, []);
121
+ });
122
+ test("ignores Like on another bot's local object", async () => {
123
+ const { instance, alpha, beta } = createHarness();
124
+ const liked = [];
125
+ alpha.onLike = (session) => void liked.push(session.bot.identifier);
126
+ beta.onLike = (session) => void liked.push(session.bot.identifier);
127
+ const ctx = createMockInboxContext(instance, "https://example.com/", void 0, "beta");
128
+ const messageId = "01941f29-7c00-7fe8-ab0a-7b593990a3c1";
129
+ await instance.onLiked(ctx, new Like({
130
+ id: new URL("https://remote.example/likes/4"),
131
+ actor: remotePerson("john"),
132
+ object: new Note({
133
+ id: new URL(`https://example.com/ap/actor/alpha/note/${messageId}`),
134
+ attribution: new URL("https://example.com/ap/actor/alpha"),
135
+ to: PUBLIC_COLLECTION,
136
+ content: "Alpha's note"
137
+ })
138
+ }));
139
+ assert.deepStrictEqual(liked, []);
140
+ });
141
+ test("ignores reactions on another bot's local object", async () => {
142
+ const { instance, alpha, beta } = createHarness();
143
+ const reacted = [];
144
+ alpha.onReact = (session) => void reacted.push(session.bot.identifier);
145
+ beta.onReact = (session) => void reacted.push(session.bot.identifier);
146
+ const ctx = createMockInboxContext(instance, "https://example.com/", void 0, "beta");
147
+ const messageId = "01941f29-7c00-7fe8-ab0a-7b593990a3c2";
148
+ await instance.onLiked(ctx, new Like({
149
+ id: new URL("https://remote.example/reacts/1"),
150
+ actor: remotePerson("john"),
151
+ name: "👍",
152
+ object: new Note({
153
+ id: new URL(`https://example.com/ap/actor/alpha/note/${messageId}`),
154
+ attribution: new URL("https://example.com/ap/actor/alpha"),
155
+ to: PUBLIC_COLLECTION,
156
+ content: "Alpha's note"
157
+ })
158
+ }));
159
+ assert.deepStrictEqual(reacted, []);
160
+ });
161
+ test("routes Create replies to the replied bot", async () => {
162
+ const { instance, repository, alpha, beta, ctx } = createHarness();
163
+ const events = [];
164
+ alpha.onReply = (session) => void events.push(`reply:${session.bot.identifier}`);
165
+ beta.onReply = (session) => void events.push(`reply:${session.bot.identifier}`);
166
+ beta.onMessage = (session) => void events.push(`message:${session.bot.identifier}`);
167
+ const messageId = "01941f29-7c00-7fe8-ab0a-7b593990a3c0";
168
+ await repository.addMessage("alpha", messageId, new Create({
169
+ id: new URL(`https://example.com/ap/actor/alpha/create/${messageId}`),
170
+ actor: new URL("https://example.com/ap/actor/alpha"),
171
+ to: PUBLIC_COLLECTION,
172
+ object: new Note({
173
+ id: new URL(`https://example.com/ap/actor/alpha/note/${messageId}`),
174
+ attribution: new URL("https://example.com/ap/actor/alpha"),
175
+ to: PUBLIC_COLLECTION,
176
+ content: "Original post"
177
+ })
178
+ }));
179
+ const author = remotePerson("john");
180
+ await instance.onCreated(ctx, new Create({
181
+ id: new URL("https://remote.example/creates/1"),
182
+ actor: author,
183
+ to: PUBLIC_COLLECTION,
184
+ object: new Note({
185
+ id: new URL("https://remote.example/notes/1"),
186
+ attribution: author,
187
+ to: PUBLIC_COLLECTION,
188
+ content: "A reply",
189
+ replyTarget: new URL(`https://example.com/ap/actor/alpha/note/${messageId}`)
190
+ })
191
+ }));
192
+ assert.deepStrictEqual(events, ["reply:alpha"]);
193
+ });
194
+ test("routes Create mentions to the mentioned bot", async () => {
195
+ const { instance, alpha, beta, ctx } = createHarness();
196
+ const events = [];
197
+ alpha.onMention = (session) => void events.push(`mention:${session.bot.identifier}`);
198
+ alpha.onMessage = (session) => void events.push(`message:${session.bot.identifier}`);
199
+ beta.onMention = (session) => void events.push(`mention:${session.bot.identifier}`);
200
+ const author = remotePerson("john");
201
+ await instance.onCreated(ctx, new Create({
202
+ id: new URL("https://remote.example/creates/2"),
203
+ actor: author,
204
+ to: PUBLIC_COLLECTION,
205
+ object: new Note({
206
+ id: new URL("https://remote.example/notes/2"),
207
+ attribution: author,
208
+ to: PUBLIC_COLLECTION,
209
+ content: "Hello @betabot!",
210
+ tags: [new Mention({
211
+ href: new URL("https://example.com/ap/actor/beta"),
212
+ name: "@betabot@example.com"
213
+ })]
214
+ })
215
+ }));
216
+ assert.deepStrictEqual(events, ["mention:beta"]);
217
+ });
218
+ test("routes Create to followers of the author", async () => {
219
+ const { instance, repository, alpha, beta, ctx } = createHarness();
220
+ const events = [];
221
+ alpha.onMessage = (session) => void events.push(`message:${session.bot.identifier}`);
222
+ beta.onMessage = (session) => void events.push(`message:${session.bot.identifier}`);
223
+ const author = remotePerson("john");
224
+ await repository.addFollowee("alpha", author.id, new Follow({
225
+ id: new URL("https://example.com/ap/actor/alpha/follow/e35ff5d8-ede9-4f5e-9b83-4bfcd4c9a69c"),
226
+ actor: new URL("https://example.com/ap/actor/alpha"),
227
+ object: author.id
228
+ }));
229
+ await instance.onCreated(ctx, new Create({
230
+ id: new URL("https://remote.example/creates/5"),
231
+ actor: author,
232
+ to: PUBLIC_COLLECTION,
233
+ object: new Note({
234
+ id: new URL("https://remote.example/notes/5"),
235
+ attribution: author,
236
+ to: PUBLIC_COLLECTION,
237
+ content: "Just a timeline post"
238
+ })
239
+ }));
240
+ assert.deepStrictEqual(events, ["message:alpha"]);
241
+ });
242
+ test("routes Undo(Follow) to the unfollowed bot", async () => {
243
+ const { instance, repository, alpha, beta, ctx } = createHarness();
244
+ const unfollowed = [];
245
+ alpha.onUnfollow = (session) => void unfollowed.push(session.bot.identifier);
246
+ beta.onUnfollow = (session) => void unfollowed.push(session.bot.identifier);
247
+ const follower = remotePerson("john");
248
+ const followId = new URL("https://remote.example/follows/1");
249
+ await repository.addFollower("alpha", followId, follower);
250
+ await instance.onUndone(ctx, new Undo({
251
+ actor: follower.id,
252
+ object: new Follow({
253
+ id: followId,
254
+ actor: follower.id,
255
+ object: new URL("https://example.com/ap/actor/alpha")
256
+ })
257
+ }));
258
+ assert.deepStrictEqual(unfollowed, ["alpha"]);
259
+ });
260
+ test("delivers personal inbox activities to the recipient only", async () => {
261
+ const repository = new MemoryRepository();
262
+ const instance = new InstanceImpl({
263
+ kv: new MemoryKvStore(),
264
+ repository
265
+ });
266
+ const alpha = instance.createBot("alpha", { username: "alphabot" });
267
+ const beta = instance.createBot("beta", { username: "betabot" });
268
+ const events = [];
269
+ alpha.onMessage = (session) => void events.push(`message:${session.bot.identifier}`);
270
+ beta.onMessage = (session) => void events.push(`message:${session.bot.identifier}`);
271
+ const ctx = createMockInboxContext(instance, "https://example.com/", void 0, "beta");
272
+ const author = remotePerson("john");
273
+ await instance.onCreated(ctx, new Create({
274
+ id: new URL("https://remote.example/creates/3"),
275
+ actor: author,
276
+ to: PUBLIC_COLLECTION,
277
+ object: new Note({
278
+ id: new URL("https://remote.example/notes/3"),
279
+ attribution: author,
280
+ to: PUBLIC_COLLECTION,
281
+ content: "Delivered to beta's personal inbox"
282
+ })
283
+ }));
284
+ assert.deepStrictEqual(events, ["message:beta"]);
285
+ });
286
+ });
287
+ describe("compatible single-bot instances", () => {
288
+ test("fire onMessage for any incoming Create", async () => {
289
+ const bot = new BotImpl({
290
+ kv: new MemoryKvStore(),
291
+ repository: new MemoryRepository(),
292
+ username: "bot"
293
+ });
294
+ const events = [];
295
+ bot.onMessage = (session) => void events.push(`message:${session.bot.identifier}`);
296
+ const ctx = createMockInboxContext(bot.instance, "https://example.com/", void 0);
297
+ const author = remotePerson("john");
298
+ await bot.instance.onCreated(ctx, new Create({
299
+ id: new URL("https://remote.example/creates/4"),
300
+ actor: author,
301
+ to: PUBLIC_COLLECTION,
302
+ object: new Note({
303
+ id: new URL("https://remote.example/notes/4"),
304
+ attribution: author,
305
+ to: PUBLIC_COLLECTION,
306
+ content: "Unrelated post"
307
+ })
308
+ }));
309
+ assert.deepStrictEqual(events, ["message:bot"]);
310
+ });
311
+ test("fire onLike for likes of objects with foreign URIs", async () => {
312
+ const bot = new BotImpl({
313
+ kv: new MemoryKvStore(),
314
+ repository: new MemoryRepository(),
315
+ username: "bot"
316
+ });
317
+ const events = [];
318
+ bot.onLike = (session) => void events.push(session.bot.identifier);
319
+ const ctx = createMockInboxContext(bot.instance, "https://example.com/", void 0);
320
+ await bot.instance.onLiked(ctx, new Like({
321
+ id: new URL("https://remote.example/likes/3"),
322
+ actor: new Person({
323
+ id: new URL("https://example.com/ap/actor/bot"),
324
+ preferredUsername: "bot"
325
+ }),
326
+ object: new Note({
327
+ id: new URL("https://remote.example/notes/5"),
328
+ attribution: new URL("https://example.com/ap/actor/bot"),
329
+ to: PUBLIC_COLLECTION,
330
+ content: "A note with a foreign URI"
331
+ })
332
+ }));
333
+ assert.deepStrictEqual(events, ["bot"]);
334
+ });
335
+ });
336
+ describe("addressed Create routing", () => {
337
+ test("routes by the embedded object's addressing", async () => {
338
+ const repository = new MemoryRepository();
339
+ const instance = new InstanceImpl({
340
+ kv: new MemoryKvStore(),
341
+ repository
342
+ });
343
+ const alpha = instance.createBot("alpha", { username: "alphabot" });
344
+ const beta = instance.createBot("beta", { username: "betabot" });
345
+ const events = [];
346
+ alpha.onMessage = (session) => void events.push(`message:${session.bot.identifier}`);
347
+ beta.onMessage = (session) => void events.push(`message:${session.bot.identifier}`);
348
+ const ctx = createMockInboxContext(instance, "https://example.com/", void 0);
349
+ const author = remotePerson("john");
350
+ await instance.onCreated(ctx, new Create({
351
+ id: new URL("https://remote.example/creates/6"),
352
+ actor: author,
353
+ to: PUBLIC_COLLECTION,
354
+ object: new Note({
355
+ id: new URL("https://remote.example/notes/6"),
356
+ attribution: author,
357
+ to: new URL("https://example.com/ap/actor/beta"),
358
+ cc: PUBLIC_COLLECTION,
359
+ content: "Addressed to beta on the object"
360
+ })
361
+ }));
362
+ assert.deepStrictEqual(events, ["message:beta"]);
363
+ });
364
+ });
365
+
366
+ //#endregion
367
+ //# sourceMappingURL=instance-routing.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance-routing.test.js","names":["instance: InstanceImpl<TContextData>","origin: string | URL","contextData: TContextData","recipient?: string | null","handle: string","followed: string[]","accepted: string[]","followId: Uuid","liked: string[]","messageId: Uuid","RawLike","reacted: string[]","events: string[]","unfollowed: string[]"],"sources":["../src/instance-routing.test.ts"],"sourcesContent":["// BotKit by Fedify: A framework for creating ActivityPub bots\n// Copyright (C) 2025–2026 Hong Minhee <https://hongminhee.org/>\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\nimport { type InboxContext, MemoryKvStore } from \"@fedify/fedify/federation\";\nimport {\n Accept,\n Create,\n Follow,\n Like as RawLike,\n Mention,\n Note,\n Person,\n PUBLIC_COLLECTION,\n Undo,\n} from \"@fedify/vocab\";\nimport assert from \"node:assert\";\nimport { describe, test } from \"node:test\";\nimport type { Bot } from \"./bot.ts\";\nimport { BotImpl } from \"./bot-impl.ts\";\nimport { InstanceImpl } from \"./instance-impl.ts\";\nimport { MemoryRepository, type Uuid } from \"./repository.ts\";\n\nfunction createMockInboxContext<TContextData>(\n instance: InstanceImpl<TContextData>,\n origin: string | URL,\n contextData: TContextData,\n recipient?: string | null,\n): InboxContext<TContextData> {\n const ctx = instance.federation.createContext(\n new URL(origin),\n contextData,\n ) as InboxContext<TContextData>;\n Object.defineProperty(ctx, \"recipient\", {\n value: recipient ?? null,\n writable: true,\n configurable: true,\n });\n Object.defineProperty(ctx, \"sendActivity\", {\n value: () => Promise.resolve(),\n writable: true,\n configurable: true,\n });\n Object.defineProperty(ctx, \"forwardActivity\", {\n value: () => Promise.resolve(),\n writable: true,\n configurable: true,\n });\n return ctx;\n}\n\ninterface Harness {\n readonly instance: InstanceImpl<void>;\n readonly repository: MemoryRepository;\n readonly alpha: Bot<void>;\n readonly beta: Bot<void>;\n readonly ctx: InboxContext<void>;\n}\n\nfunction createHarness(): Harness {\n const repository = new MemoryRepository();\n const instance = new InstanceImpl<void>({\n kv: new MemoryKvStore(),\n repository,\n });\n const alpha = instance.createBot(\"alpha\", { username: \"alphabot\" });\n const beta = instance.createBot(\"beta\", { username: \"betabot\" });\n const ctx = createMockInboxContext(\n instance,\n \"https://example.com/\",\n undefined,\n );\n return { instance, repository, alpha, beta, ctx };\n}\n\nfunction remotePerson(handle: string): Person {\n return new Person({\n id: new URL(`https://example.com/ap/actor/${handle}`),\n preferredUsername: handle,\n });\n}\n\ndescribe(\"shared inbox routing\", () => {\n test(\"routes Follow to the followed bot only\", async () => {\n const { instance, alpha, beta, ctx } = createHarness();\n const followed: string[] = [];\n alpha.onFollow = (session) => void (followed.push(session.bot.identifier));\n beta.onFollow = (session) => void (followed.push(session.bot.identifier));\n await instance.onFollowed(\n ctx,\n new Follow({\n id: new URL(\"https://remote.example/follows/1\"),\n actor: remotePerson(\"john\"),\n object: new URL(\"https://example.com/ap/actor/alpha\"),\n }),\n );\n assert.deepStrictEqual(followed, [\"alpha\"]);\n });\n\n test(\"routes Accept to the bot that sent the follow\", async () => {\n const { instance, repository, alpha, beta, ctx } = createHarness();\n const accepted: string[] = [];\n alpha.onAcceptFollow = (session) =>\n void (accepted.push(session.bot.identifier));\n beta.onAcceptFollow = (session) =>\n void (accepted.push(session.bot.identifier));\n const followId: Uuid = \"9d952a10-77e6-46bd-a48a-208b47e5e2bb\";\n await repository.addSentFollow(\n \"alpha\",\n followId,\n new Follow({\n id: new URL(\n `https://example.com/ap/actor/alpha/follow/${followId}`,\n ),\n actor: new URL(\"https://example.com/ap/actor/alpha\"),\n object: remotePerson(\"john\"),\n }),\n );\n await instance.onFollowAccepted(\n ctx,\n new Accept({\n actor: new URL(\"https://example.com/ap/actor/john\"),\n object: new URL(\n `https://example.com/ap/actor/alpha/follow/${followId}`,\n ),\n }),\n );\n assert.deepStrictEqual(accepted, [\"alpha\"]);\n });\n\n test(\"routes Like to the bot owning the message\", async () => {\n const { instance, repository, alpha, beta, ctx } = createHarness();\n const liked: string[] = [];\n alpha.onLike = (session) => void (liked.push(session.bot.identifier));\n beta.onLike = (session) => void (liked.push(session.bot.identifier));\n const messageId: Uuid = \"01941f29-7c00-7fe8-ab0a-7b593990a3c0\";\n await repository.addMessage(\n \"alpha\",\n messageId,\n new Create({\n id: new URL(\n `https://example.com/ap/actor/alpha/create/${messageId}`,\n ),\n actor: new URL(\"https://example.com/ap/actor/alpha\"),\n to: PUBLIC_COLLECTION,\n object: new Note({\n id: new URL(\n `https://example.com/ap/actor/alpha/note/${messageId}`,\n ),\n attribution: new URL(\"https://example.com/ap/actor/alpha\"),\n to: PUBLIC_COLLECTION,\n content: \"Hello!\",\n }),\n }),\n );\n await instance.onLiked(\n ctx,\n new RawLike({\n id: new URL(\"https://remote.example/likes/1\"),\n actor: remotePerson(\"john\"),\n object: new URL(\n `https://example.com/ap/actor/alpha/note/${messageId}`,\n ),\n }),\n );\n assert.deepStrictEqual(liked, [\"alpha\"]);\n });\n\n test(\"drops Like on objects the instance does not own\", async () => {\n const { instance, alpha, beta, ctx } = createHarness();\n const liked: string[] = [];\n alpha.onLike = (session) => void (liked.push(session.bot.identifier));\n beta.onLike = (session) => void (liked.push(session.bot.identifier));\n await instance.onLiked(\n ctx,\n new RawLike({\n id: new URL(\"https://remote.example/likes/2\"),\n actor: remotePerson(\"john\"),\n object: new URL(\"https://remote.example/notes/1\"),\n }),\n );\n assert.deepStrictEqual(liked, []);\n });\n\n test(\"ignores Like on another bot's local object\", async () => {\n const { instance, alpha, beta } = createHarness();\n const liked: string[] = [];\n alpha.onLike = (session) => void (liked.push(session.bot.identifier));\n beta.onLike = (session) => void (liked.push(session.bot.identifier));\n // Delivered to beta's personal inbox, but the object is alpha's local\n // note (embedded, so no dereference is needed to see it):\n const ctx = createMockInboxContext(\n instance,\n \"https://example.com/\",\n undefined,\n \"beta\",\n );\n const messageId: Uuid = \"01941f29-7c00-7fe8-ab0a-7b593990a3c1\";\n await instance.onLiked(\n ctx,\n new RawLike({\n id: new URL(\"https://remote.example/likes/4\"),\n actor: remotePerson(\"john\"),\n object: new Note({\n id: new URL(\n `https://example.com/ap/actor/alpha/note/${messageId}`,\n ),\n attribution: new URL(\"https://example.com/ap/actor/alpha\"),\n to: PUBLIC_COLLECTION,\n content: \"Alpha's note\",\n }),\n }),\n );\n assert.deepStrictEqual(liked, []);\n });\n\n test(\"ignores reactions on another bot's local object\", async () => {\n const { instance, alpha, beta } = createHarness();\n const reacted: string[] = [];\n alpha.onReact = (session) => void (reacted.push(session.bot.identifier));\n beta.onReact = (session) => void (reacted.push(session.bot.identifier));\n const ctx = createMockInboxContext(\n instance,\n \"https://example.com/\",\n undefined,\n \"beta\",\n );\n const messageId: Uuid = \"01941f29-7c00-7fe8-ab0a-7b593990a3c2\";\n // A Like with a name is treated as an emoji reaction:\n await instance.onLiked(\n ctx,\n new RawLike({\n id: new URL(\"https://remote.example/reacts/1\"),\n actor: remotePerson(\"john\"),\n name: \"👍\",\n object: new Note({\n id: new URL(\n `https://example.com/ap/actor/alpha/note/${messageId}`,\n ),\n attribution: new URL(\"https://example.com/ap/actor/alpha\"),\n to: PUBLIC_COLLECTION,\n content: \"Alpha's note\",\n }),\n }),\n );\n assert.deepStrictEqual(reacted, []);\n });\n\n test(\"routes Create replies to the replied bot\", async () => {\n const { instance, repository, alpha, beta, ctx } = createHarness();\n const events: string[] = [];\n alpha.onReply = (session) =>\n void (events.push(`reply:${session.bot.identifier}`));\n beta.onReply = (session) =>\n void (events.push(`reply:${session.bot.identifier}`));\n beta.onMessage = (session) =>\n void (events.push(`message:${session.bot.identifier}`));\n\n const messageId: Uuid = \"01941f29-7c00-7fe8-ab0a-7b593990a3c0\";\n await repository.addMessage(\n \"alpha\",\n messageId,\n new Create({\n id: new URL(\n `https://example.com/ap/actor/alpha/create/${messageId}`,\n ),\n actor: new URL(\"https://example.com/ap/actor/alpha\"),\n to: PUBLIC_COLLECTION,\n object: new Note({\n id: new URL(\n `https://example.com/ap/actor/alpha/note/${messageId}`,\n ),\n attribution: new URL(\"https://example.com/ap/actor/alpha\"),\n to: PUBLIC_COLLECTION,\n content: \"Original post\",\n }),\n }),\n );\n\n const author = remotePerson(\"john\");\n await instance.onCreated(\n ctx,\n new Create({\n id: new URL(\"https://remote.example/creates/1\"),\n actor: author,\n to: PUBLIC_COLLECTION,\n object: new Note({\n id: new URL(\"https://remote.example/notes/1\"),\n attribution: author,\n to: PUBLIC_COLLECTION,\n content: \"A reply\",\n replyTarget: new URL(\n `https://example.com/ap/actor/alpha/note/${messageId}`,\n ),\n }),\n }),\n );\n assert.deepStrictEqual(events, [\"reply:alpha\"]);\n });\n\n test(\"routes Create mentions to the mentioned bot\", async () => {\n const { instance, alpha, beta, ctx } = createHarness();\n const events: string[] = [];\n alpha.onMention = (session) =>\n void (events.push(`mention:${session.bot.identifier}`));\n alpha.onMessage = (session) =>\n void (events.push(`message:${session.bot.identifier}`));\n beta.onMention = (session) =>\n void (events.push(`mention:${session.bot.identifier}`));\n\n const author = remotePerson(\"john\");\n await instance.onCreated(\n ctx,\n new Create({\n id: new URL(\"https://remote.example/creates/2\"),\n actor: author,\n to: PUBLIC_COLLECTION,\n object: new Note({\n id: new URL(\"https://remote.example/notes/2\"),\n attribution: author,\n to: PUBLIC_COLLECTION,\n content: \"Hello @betabot!\",\n tags: [\n new Mention({\n href: new URL(\"https://example.com/ap/actor/beta\"),\n name: \"@betabot@example.com\",\n }),\n ],\n }),\n }),\n );\n assert.deepStrictEqual(events, [\"mention:beta\"]);\n });\n\n test(\"routes Create to followers of the author\", async () => {\n const { instance, repository, alpha, beta, ctx } = createHarness();\n const events: string[] = [];\n alpha.onMessage = (session) =>\n void (events.push(`message:${session.bot.identifier}`));\n beta.onMessage = (session) =>\n void (events.push(`message:${session.bot.identifier}`));\n\n const author = remotePerson(\"john\");\n await repository.addFollowee(\n \"alpha\",\n author.id!,\n new Follow({\n id: new URL(\n \"https://example.com/ap/actor/alpha/follow/e35ff5d8-ede9-4f5e-9b83-4bfcd4c9a69c\",\n ),\n actor: new URL(\"https://example.com/ap/actor/alpha\"),\n object: author.id!,\n }),\n );\n await instance.onCreated(\n ctx,\n new Create({\n id: new URL(\"https://remote.example/creates/5\"),\n actor: author,\n to: PUBLIC_COLLECTION,\n object: new Note({\n id: new URL(\"https://remote.example/notes/5\"),\n attribution: author,\n to: PUBLIC_COLLECTION,\n content: \"Just a timeline post\",\n }),\n }),\n );\n assert.deepStrictEqual(events, [\"message:alpha\"]);\n });\n\n test(\"routes Undo(Follow) to the unfollowed bot\", async () => {\n const { instance, repository, alpha, beta, ctx } = createHarness();\n const unfollowed: string[] = [];\n alpha.onUnfollow = (session) =>\n void (unfollowed.push(session.bot.identifier));\n beta.onUnfollow = (session) =>\n void (unfollowed.push(session.bot.identifier));\n const follower = remotePerson(\"john\");\n const followId = new URL(\"https://remote.example/follows/1\");\n await repository.addFollower(\"alpha\", followId, follower);\n await instance.onUndone(\n ctx,\n new Undo({\n actor: follower.id,\n object: new Follow({\n id: followId,\n actor: follower.id,\n object: new URL(\"https://example.com/ap/actor/alpha\"),\n }),\n }),\n );\n assert.deepStrictEqual(unfollowed, [\"alpha\"]);\n });\n\n test(\"delivers personal inbox activities to the recipient only\", async () => {\n const repository = new MemoryRepository();\n const instance = new InstanceImpl<void>({\n kv: new MemoryKvStore(),\n repository,\n });\n const alpha = instance.createBot(\"alpha\", { username: \"alphabot\" });\n const beta = instance.createBot(\"beta\", { username: \"betabot\" });\n const events: string[] = [];\n alpha.onMessage = (session) =>\n void (events.push(`message:${session.bot.identifier}`));\n beta.onMessage = (session) =>\n void (events.push(`message:${session.bot.identifier}`));\n const ctx = createMockInboxContext(\n instance,\n \"https://example.com/\",\n undefined,\n \"beta\",\n );\n const author = remotePerson(\"john\");\n await instance.onCreated(\n ctx,\n new Create({\n id: new URL(\"https://remote.example/creates/3\"),\n actor: author,\n to: PUBLIC_COLLECTION,\n object: new Note({\n id: new URL(\"https://remote.example/notes/3\"),\n attribution: author,\n to: PUBLIC_COLLECTION,\n content: \"Delivered to beta's personal inbox\",\n }),\n }),\n );\n assert.deepStrictEqual(events, [\"message:beta\"]);\n });\n});\n\ndescribe(\"compatible single-bot instances\", () => {\n test(\"fire onMessage for any incoming Create\", async () => {\n const bot = new BotImpl<void>({\n kv: new MemoryKvStore(),\n repository: new MemoryRepository(),\n username: \"bot\",\n });\n const events: string[] = [];\n bot.onMessage = (session) =>\n void (events.push(`message:${session.bot.identifier}`));\n const ctx = createMockInboxContext(\n bot.instance,\n \"https://example.com/\",\n undefined,\n );\n const author = remotePerson(\"john\");\n // The bot does not follow the author and is not addressed, but the\n // pre-0.5 behavior of a single-bot deployment is preserved:\n await bot.instance.onCreated(\n ctx,\n new Create({\n id: new URL(\"https://remote.example/creates/4\"),\n actor: author,\n to: PUBLIC_COLLECTION,\n object: new Note({\n id: new URL(\"https://remote.example/notes/4\"),\n attribution: author,\n to: PUBLIC_COLLECTION,\n content: \"Unrelated post\",\n }),\n }),\n );\n assert.deepStrictEqual(events, [\"message:bot\"]);\n });\n\n test(\"fire onLike for likes of objects with foreign URIs\", async () => {\n const bot = new BotImpl<void>({\n kv: new MemoryKvStore(),\n repository: new MemoryRepository(),\n username: \"bot\",\n });\n const events: string[] = [];\n bot.onLike = (session) => void (events.push(session.bot.identifier));\n const ctx = createMockInboxContext(\n bot.instance,\n \"https://example.com/\",\n undefined,\n );\n await bot.instance.onLiked(\n ctx,\n new RawLike({\n id: new URL(\"https://remote.example/likes/3\"),\n actor: new Person({\n id: new URL(\"https://example.com/ap/actor/bot\"),\n preferredUsername: \"bot\",\n }),\n object: new Note({\n id: new URL(\"https://remote.example/notes/5\"),\n attribution: new URL(\"https://example.com/ap/actor/bot\"),\n to: PUBLIC_COLLECTION,\n content: \"A note with a foreign URI\",\n }),\n }),\n );\n assert.deepStrictEqual(events, [\"bot\"]);\n });\n});\n\ndescribe(\"addressed Create routing\", () => {\n test(\"routes by the embedded object's addressing\", async () => {\n const repository = new MemoryRepository();\n const instance = new InstanceImpl<void>({\n kv: new MemoryKvStore(),\n repository,\n });\n const alpha = instance.createBot(\"alpha\", { username: \"alphabot\" });\n const beta = instance.createBot(\"beta\", { username: \"betabot\" });\n const events: string[] = [];\n alpha.onMessage = (session) =>\n void (events.push(`message:${session.bot.identifier}`));\n beta.onMessage = (session) =>\n void (events.push(`message:${session.bot.identifier}`));\n const ctx = createMockInboxContext(\n instance,\n \"https://example.com/\",\n undefined,\n );\n const author = remotePerson(\"john\");\n // The activity wrapper is public only; the audience is on the object:\n await instance.onCreated(\n ctx,\n new Create({\n id: new URL(\"https://remote.example/creates/6\"),\n actor: author,\n to: PUBLIC_COLLECTION,\n object: new Note({\n id: new URL(\"https://remote.example/notes/6\"),\n attribution: author,\n to: new URL(\"https://example.com/ap/actor/beta\"),\n cc: PUBLIC_COLLECTION,\n content: \"Addressed to beta on the object\",\n }),\n }),\n );\n assert.deepStrictEqual(events, [\"message:beta\"]);\n });\n});\n"],"mappings":"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"}