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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/bot-group.test.d.ts +2 -0
  2. package/dist/bot-group.test.js +220 -0
  3. package/dist/bot-group.test.js.map +1 -0
  4. package/dist/bot-impl.d.ts +132 -13
  5. package/dist/bot-impl.d.ts.map +1 -1
  6. package/dist/bot-impl.js +400 -178
  7. package/dist/bot-impl.js.map +1 -1
  8. package/dist/bot-impl.test.js +214 -76
  9. package/dist/bot-impl.test.js.map +1 -1
  10. package/dist/bot.d.ts +94 -48
  11. package/dist/bot.d.ts.map +1 -1
  12. package/dist/bot.js +2 -104
  13. package/dist/bot.js.map +1 -1
  14. package/dist/bot.test.js +59 -0
  15. package/dist/bot.test.js.map +1 -1
  16. package/dist/components/FollowButton.d.ts +5 -3
  17. package/dist/components/FollowButton.d.ts.map +1 -1
  18. package/dist/components/FollowButton.js +2 -2
  19. package/dist/components/FollowButton.js.map +1 -1
  20. package/dist/components/Follower.d.ts +2 -2
  21. package/dist/components/Layout.js +1 -1
  22. package/dist/components/Layout.js.map +1 -1
  23. package/dist/components/Message.d.ts +2 -2
  24. package/dist/deno.js +2 -1
  25. package/dist/deno.js.map +1 -1
  26. package/dist/follow-impl.test.js +3 -3
  27. package/dist/follow-impl.test.js.map +1 -1
  28. package/dist/instance-impl.d.ts +158 -0
  29. package/dist/instance-impl.d.ts.map +1 -0
  30. package/dist/instance-impl.js +603 -0
  31. package/dist/instance-impl.js.map +1 -0
  32. package/dist/instance-impl.test.d.ts +2 -0
  33. package/dist/instance-impl.test.js +103 -0
  34. package/dist/instance-impl.test.js.map +1 -0
  35. package/dist/instance-multi.test.d.ts +2 -0
  36. package/dist/instance-multi.test.js +151 -0
  37. package/dist/instance-multi.test.js.map +1 -0
  38. package/dist/instance-routing.test.d.ts +2 -0
  39. package/dist/instance-routing.test.js +367 -0
  40. package/dist/instance-routing.test.js.map +1 -0
  41. package/dist/instance.d.ts +318 -0
  42. package/dist/instance.d.ts.map +1 -0
  43. package/dist/instance.js +51 -0
  44. package/dist/instance.js.map +1 -0
  45. package/dist/message-impl.d.ts.map +1 -1
  46. package/dist/message-impl.js +17 -10
  47. package/dist/message-impl.js.map +1 -1
  48. package/dist/message-impl.test.js +43 -9
  49. package/dist/message-impl.test.js.map +1 -1
  50. package/dist/mod.d.ts +5 -3
  51. package/dist/mod.js +4 -2
  52. package/dist/pages.d.ts +12 -3
  53. package/dist/pages.d.ts.map +1 -1
  54. package/dist/pages.js +112 -41
  55. package/dist/pages.js.map +1 -1
  56. package/dist/pages.test.d.ts +2 -0
  57. package/dist/pages.test.js +170 -0
  58. package/dist/pages.test.js.map +1 -0
  59. package/dist/repository.d.ts +385 -138
  60. package/dist/repository.d.ts.map +1 -1
  61. package/dist/repository.js +595 -223
  62. package/dist/repository.js.map +1 -1
  63. package/dist/repository.test.js +564 -136
  64. package/dist/repository.test.js.map +1 -1
  65. package/dist/session-impl.d.ts.map +1 -1
  66. package/dist/session-impl.js +13 -4
  67. package/dist/session-impl.js.map +1 -1
  68. package/dist/session-impl.test.d.ts.map +1 -1
  69. package/dist/session-impl.test.js +9 -9
  70. package/dist/session-impl.test.js.map +1 -1
  71. package/dist/session.d.ts +8 -3
  72. package/dist/session.d.ts.map +1 -1
  73. package/dist/text.d.ts.map +1 -1
  74. package/dist/text.js +27 -10
  75. package/dist/text.js.map +1 -1
  76. package/dist/text.test.js +37 -2
  77. package/dist/text.test.js.map +1 -1
  78. package/dist/uri.d.ts +46 -0
  79. package/dist/uri.d.ts.map +1 -0
  80. package/dist/uri.js +64 -0
  81. package/dist/uri.js.map +1 -0
  82. package/dist/uri.test.d.ts +2 -0
  83. package/dist/uri.test.js +93 -0
  84. package/dist/uri.test.js.map +1 -0
  85. package/package.json +5 -1
@@ -0,0 +1,2 @@
1
+ import { Temporal, toTemporalInstant } from "@js-temporal/polyfill";
2
+ Date.prototype.toTemporalInstant = toTemporalInstant;
@@ -0,0 +1,220 @@
1
+
2
+ import { Temporal, toTemporalInstant } from "@js-temporal/polyfill";
3
+ Date.prototype.toTemporalInstant = toTemporalInstant;
4
+
5
+ import { MemoryRepository } from "./repository.js";
6
+ import { InstanceImpl } from "./instance-impl.js";
7
+ import { MemoryKvStore } from "@fedify/fedify/federation";
8
+ import { Create, Follow, Note, PUBLIC_COLLECTION, Person } from "@fedify/vocab";
9
+ import assert from "node:assert";
10
+ import { describe, test } from "node:test";
11
+
12
+ //#region src/bot-group.test.ts
13
+ function createInstance() {
14
+ const repository = new MemoryRepository();
15
+ const instance = new InstanceImpl({
16
+ kv: new MemoryKvStore(),
17
+ repository
18
+ });
19
+ return {
20
+ instance,
21
+ repository
22
+ };
23
+ }
24
+ function regionProfile(identifier) {
25
+ if (!identifier.startsWith("region_")) return null;
26
+ const region = identifier.slice(7);
27
+ return {
28
+ username: identifier,
29
+ name: `${region.toUpperCase()} Weather Bot`
30
+ };
31
+ }
32
+ describe("dynamic bots", () => {
33
+ test("serve dispatcher-resolved actors", async () => {
34
+ const { instance } = createInstance();
35
+ let calls = 0;
36
+ instance.createBot((_ctx, identifier) => {
37
+ calls++;
38
+ return regionProfile(identifier);
39
+ });
40
+ const response = await instance.fetch(new Request("https://example.com/ap/actor/region_kr", { headers: { Accept: "application/activity+json" } }), void 0);
41
+ assert.deepStrictEqual(response.status, 200);
42
+ const actor = await response.json();
43
+ assert.deepStrictEqual(actor.preferredUsername, "region_kr");
44
+ assert.deepStrictEqual(actor.name, "KR Weather Bot");
45
+ const miss = await instance.fetch(new Request("https://example.com/ap/actor/other", { headers: { Accept: "application/activity+json" } }), void 0);
46
+ assert.deepStrictEqual(miss.status, 404);
47
+ calls = 0;
48
+ const ctx = instance.federation.createContext(new URL("https://example.com/"), void 0);
49
+ await instance.resolveBot(ctx, "region_kr");
50
+ await instance.resolveBot(ctx, "region_kr");
51
+ await instance.resolveBot(ctx, "other");
52
+ await instance.resolveBot(ctx, "other");
53
+ assert.deepStrictEqual(calls, 2);
54
+ });
55
+ test("prefer static bots over dynamic dispatchers", async () => {
56
+ const { instance } = createInstance();
57
+ instance.createBot("region_kr", { username: "static-kr" });
58
+ instance.createBot((_ctx, identifier) => regionProfile(identifier));
59
+ const response = await instance.fetch(new Request("https://example.com/ap/actor/region_kr", { headers: { Accept: "application/activity+json" } }), void 0);
60
+ assert.deepStrictEqual(response.status, 200);
61
+ const actor = await response.json();
62
+ assert.deepStrictEqual(actor.preferredUsername, "static-kr");
63
+ });
64
+ test("probe dispatchers in registration order", async () => {
65
+ const { instance } = createInstance();
66
+ const probed = [];
67
+ instance.createBot((_ctx, identifier) => {
68
+ probed.push("first");
69
+ return identifier === "dual" ? { username: "first-bot" } : null;
70
+ });
71
+ instance.createBot((_ctx, identifier) => {
72
+ probed.push("second");
73
+ return identifier === "dual" ? { username: "second-bot" } : null;
74
+ });
75
+ const response = await instance.fetch(new Request("https://example.com/ap/actor/dual", { headers: { Accept: "application/activity+json" } }), void 0);
76
+ const actor = await response.json();
77
+ assert.deepStrictEqual(actor.preferredUsername, "first-bot");
78
+ assert.ok(!probed.includes("second"));
79
+ });
80
+ test("fall through to the next dispatcher on null", async () => {
81
+ const { instance } = createInstance();
82
+ const probed = [];
83
+ instance.createBot((_ctx, identifier) => {
84
+ probed.push("first");
85
+ return identifier === "somebody-else" ? { username: "first-bot" } : null;
86
+ });
87
+ instance.createBot((_ctx, identifier) => {
88
+ probed.push("second");
89
+ return regionProfile(identifier);
90
+ });
91
+ const response = await instance.fetch(new Request("https://example.com/ap/actor/region_kr", { headers: { Accept: "application/activity+json" } }), void 0);
92
+ assert.deepStrictEqual(response.status, 200);
93
+ const actor = await response.json();
94
+ assert.deepStrictEqual(actor.preferredUsername, "region_kr");
95
+ assert.ok(probed.includes("first"));
96
+ assert.ok(probed.includes("second"));
97
+ });
98
+ test("read event handlers live from the group", async () => {
99
+ const { instance } = createInstance();
100
+ const group = instance.createBot((_ctx, identifier) => regionProfile(identifier));
101
+ await instance.fetch(new Request("https://example.com/ap/actor/region_kr", { headers: { Accept: "application/activity+json" } }), void 0);
102
+ const followed = [];
103
+ group.onFollow = (session) => void followed.push(session.bot.identifier);
104
+ const ctx = instance.federation.createContext(new URL("https://example.com/"), void 0);
105
+ Object.defineProperty(ctx, "recipient", {
106
+ value: null,
107
+ configurable: true
108
+ });
109
+ Object.defineProperty(ctx, "sendActivity", {
110
+ value: () => Promise.resolve(),
111
+ configurable: true
112
+ });
113
+ await instance.onFollowed(ctx, new Follow({
114
+ id: new URL("https://remote.example/follows/1"),
115
+ actor: new Person({
116
+ id: new URL("https://remote.example/actors/john"),
117
+ preferredUsername: "john"
118
+ }),
119
+ object: new URL("https://example.com/ap/actor/region_kr")
120
+ }));
121
+ assert.deepStrictEqual(followed, ["region_kr"]);
122
+ });
123
+ test("resolve usernames through mapUsername", async () => {
124
+ const { instance } = createInstance();
125
+ instance.createBot((_ctx, identifier) => regionProfile(identifier), { mapUsername: (_ctx, username) => username.startsWith("region_") ? username : `region_${username}` });
126
+ const response = await instance.fetch(new Request("https://example.com/.well-known/webfinger?resource=acct:kr@example.com"), void 0);
127
+ assert.deepStrictEqual(response.status, 200);
128
+ const jrd = await response.json();
129
+ const self = jrd.links.find((link) => link.rel === "self");
130
+ assert.deepStrictEqual(self.href, "https://example.com/ap/actor/region_kr");
131
+ });
132
+ test("fall back to username-as-identifier lookups", async () => {
133
+ const { instance } = createInstance();
134
+ instance.createBot((_ctx, identifier) => regionProfile(identifier));
135
+ const response = await instance.fetch(new Request("https://example.com/.well-known/webfinger?resource=acct:region_kr@example.com"), void 0);
136
+ assert.deepStrictEqual(response.status, 200);
137
+ });
138
+ test("require context data unless TContextData is void", () => {
139
+ const instance = new InstanceImpl({
140
+ kv: new MemoryKvStore(),
141
+ repository: new MemoryRepository()
142
+ });
143
+ const group = instance.createBot((_ctx, identifier) => ({ username: identifier }));
144
+ group.getSession("https://example.com", "someone").catch(() => {});
145
+ group.getSession("https://example.com", "someone", { db: "x" }).catch(() => {});
146
+ });
147
+ test("create sessions for resolved identifiers", async () => {
148
+ const { instance } = createInstance();
149
+ const group = instance.createBot((_ctx, identifier) => regionProfile(identifier));
150
+ const session = await group.getSession("https://example.com", "region_kr");
151
+ assert.deepStrictEqual(session.actorId.href, "https://example.com/ap/actor/region_kr");
152
+ assert.deepStrictEqual(session.bot.identifier, "region_kr");
153
+ assert.deepStrictEqual(session.bot.username, "region_kr");
154
+ await assert.rejects(() => group.getSession("https://example.com", "nonexistent"), TypeError);
155
+ });
156
+ });
157
+ describe("dynamic bots in routing and web pages", () => {
158
+ test("receive timeline messages via followees", async () => {
159
+ const { instance, repository } = createInstance();
160
+ const group = instance.createBot((_ctx, identifier) => regionProfile(identifier));
161
+ const events = [];
162
+ group.onMessage = (session) => void events.push(`message:${session.bot.identifier}`);
163
+ const author = new Person({
164
+ id: new URL("https://example.com/ap/actor/john"),
165
+ preferredUsername: "john"
166
+ });
167
+ await repository.addFollowee("region_kr", author.id, new Follow({
168
+ id: new URL("https://example.com/ap/actor/region_kr/follow/e35ff5d8-ede9-4f5e-9b83-4bfcd4c9a69c"),
169
+ actor: new URL("https://example.com/ap/actor/region_kr"),
170
+ object: author.id
171
+ }));
172
+ const ctx = instance.federation.createContext(new URL("https://example.com/"), void 0);
173
+ Object.defineProperty(ctx, "recipient", {
174
+ value: null,
175
+ configurable: true
176
+ });
177
+ await instance.onCreated(ctx, new Create({
178
+ id: new URL("https://remote.example/creates/1"),
179
+ actor: author,
180
+ to: PUBLIC_COLLECTION,
181
+ object: new Note({
182
+ id: new URL("https://remote.example/notes/1"),
183
+ attribution: author,
184
+ to: PUBLIC_COLLECTION,
185
+ content: "A timeline post"
186
+ })
187
+ }));
188
+ assert.deepStrictEqual(events, ["message:region_kr"]);
189
+ });
190
+ test("serve dynamic bots' web pages", async () => {
191
+ const { instance } = createInstance();
192
+ instance.createBot((_ctx, identifier) => regionProfile(identifier));
193
+ const response = await instance.fetch(new Request("https://example.com/@region_kr"), void 0);
194
+ assert.deepStrictEqual(response.status, 200);
195
+ const html = await response.text();
196
+ assert.ok(html.includes("@region_kr@example.com"));
197
+ const miss = await instance.fetch(new Request("https://example.com/@nonexistent"), void 0);
198
+ assert.deepStrictEqual(miss.status, 404);
199
+ });
200
+ });
201
+ describe("mapUsername ownership", () => {
202
+ test("cannot map usernames to other bots' identifiers", async () => {
203
+ const { instance } = createInstance();
204
+ instance.createBot("staticbot", { username: "staticbot" });
205
+ instance.createBot((_ctx, identifier) => regionProfile(identifier), { mapUsername: () => "staticbot" });
206
+ const response = await instance.fetch(new Request("https://example.com/.well-known/webfinger?resource=acct:hijack@example.com"), void 0);
207
+ assert.deepStrictEqual(response.status, 404);
208
+ });
209
+ });
210
+ describe("username-as-identifier fallback scope", () => {
211
+ test("does not expose static bots' internal identifiers", async () => {
212
+ const { instance } = createInstance();
213
+ instance.createBot("internal", { username: "mybot" });
214
+ const response = await instance.fetch(new Request("https://example.com/.well-known/webfinger?resource=acct:internal@example.com"), void 0);
215
+ assert.deepStrictEqual(response.status, 404);
216
+ });
217
+ });
218
+
219
+ //#endregion
220
+ //# sourceMappingURL=bot-group.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bot-group.test.js","names":["identifier: string","probed: string[]","followed: string[]","link: { rel: string }","events: string[]"],"sources":["../src/bot-group.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 { Create, Follow, Note, Person, PUBLIC_COLLECTION } from \"@fedify/vocab\";\nimport assert from \"node:assert\";\nimport { describe, test } from \"node:test\";\nimport { InstanceImpl } from \"./instance-impl.ts\";\nimport type { BotProfile } from \"./instance.ts\";\nimport { MemoryRepository } from \"./repository.ts\";\n\nfunction createInstance(): {\n instance: InstanceImpl<void>;\n repository: MemoryRepository;\n} {\n const repository = new MemoryRepository();\n const instance = new InstanceImpl<void>({\n kv: new MemoryKvStore(),\n repository,\n });\n return { instance, repository };\n}\n\nfunction regionProfile(identifier: string): BotProfile<void> | null {\n if (!identifier.startsWith(\"region_\")) return null;\n const region = identifier.slice(\"region_\".length);\n return {\n username: identifier,\n name: `${region.toUpperCase()} Weather Bot`,\n };\n}\n\ndescribe(\"dynamic bots\", () => {\n test(\"serve dispatcher-resolved actors\", async () => {\n const { instance } = createInstance();\n let calls = 0;\n instance.createBot((_ctx, identifier) => {\n calls++;\n return regionProfile(identifier);\n });\n const response = await instance.fetch(\n new Request(\"https://example.com/ap/actor/region_kr\", {\n headers: { Accept: \"application/activity+json\" },\n }),\n undefined,\n );\n assert.deepStrictEqual(response.status, 200);\n const actor = await response.json();\n assert.deepStrictEqual(actor.preferredUsername, \"region_kr\");\n assert.deepStrictEqual(actor.name, \"KR Weather Bot\");\n\n const miss = await instance.fetch(\n new Request(\"https://example.com/ap/actor/other\", {\n headers: { Accept: \"application/activity+json\" },\n }),\n undefined,\n );\n assert.deepStrictEqual(miss.status, 404);\n\n // Resolution is memoized per context, so a dispatcher runs at most\n // once per identifier within one context:\n calls = 0;\n const ctx = instance.federation.createContext(\n new URL(\"https://example.com/\"),\n undefined,\n );\n await instance.resolveBot(ctx, \"region_kr\");\n await instance.resolveBot(ctx, \"region_kr\");\n await instance.resolveBot(ctx, \"other\");\n await instance.resolveBot(ctx, \"other\");\n assert.deepStrictEqual(calls, 2);\n });\n\n test(\"prefer static bots over dynamic dispatchers\", async () => {\n const { instance } = createInstance();\n instance.createBot(\"region_kr\", { username: \"static-kr\" });\n instance.createBot((_ctx, identifier) => regionProfile(identifier));\n const response = await instance.fetch(\n new Request(\"https://example.com/ap/actor/region_kr\", {\n headers: { Accept: \"application/activity+json\" },\n }),\n undefined,\n );\n assert.deepStrictEqual(response.status, 200);\n const actor = await response.json();\n assert.deepStrictEqual(actor.preferredUsername, \"static-kr\");\n });\n\n test(\"probe dispatchers in registration order\", async () => {\n const { instance } = createInstance();\n const probed: string[] = [];\n instance.createBot((_ctx, identifier) => {\n probed.push(\"first\");\n return identifier === \"dual\" ? { username: \"first-bot\" } : null;\n });\n instance.createBot((_ctx, identifier) => {\n probed.push(\"second\");\n return identifier === \"dual\" ? { username: \"second-bot\" } : null;\n });\n const response = await instance.fetch(\n new Request(\"https://example.com/ap/actor/dual\", {\n headers: { Accept: \"application/activity+json\" },\n }),\n undefined,\n );\n const actor = await response.json();\n assert.deepStrictEqual(actor.preferredUsername, \"first-bot\");\n assert.ok(!probed.includes(\"second\"));\n });\n\n test(\"fall through to the next dispatcher on null\", async () => {\n const { instance } = createInstance();\n const probed: string[] = [];\n instance.createBot((_ctx, identifier) => {\n probed.push(\"first\");\n return identifier === \"somebody-else\" ? { username: \"first-bot\" } : null;\n });\n instance.createBot((_ctx, identifier) => {\n probed.push(\"second\");\n return regionProfile(identifier);\n });\n const response = await instance.fetch(\n new Request(\"https://example.com/ap/actor/region_kr\", {\n headers: { Accept: \"application/activity+json\" },\n }),\n undefined,\n );\n assert.deepStrictEqual(response.status, 200);\n const actor = await response.json();\n assert.deepStrictEqual(actor.preferredUsername, \"region_kr\");\n // The first dispatcher was probed, returned null, and the resolution\n // fell through to the second:\n assert.ok(probed.includes(\"first\"));\n assert.ok(probed.includes(\"second\"));\n });\n\n test(\"read event handlers live from the group\", async () => {\n const { instance } = createInstance();\n const group = instance.createBot(\n (_ctx, identifier) => regionProfile(identifier),\n );\n // Resolve the bot once before any handler is registered:\n await instance.fetch(\n new Request(\"https://example.com/ap/actor/region_kr\", {\n headers: { Accept: \"application/activity+json\" },\n }),\n undefined,\n );\n // A handler registered afterwards must still fire:\n const followed: string[] = [];\n group.onFollow = (session) => void (followed.push(session.bot.identifier));\n const ctx = instance.federation.createContext(\n new URL(\"https://example.com/\"),\n undefined,\n ) as InboxContext<void>;\n Object.defineProperty(ctx, \"recipient\", {\n value: null,\n configurable: true,\n });\n Object.defineProperty(ctx, \"sendActivity\", {\n value: () => Promise.resolve(),\n configurable: true,\n });\n await instance.onFollowed(\n ctx,\n new Follow({\n id: new URL(\"https://remote.example/follows/1\"),\n actor: new Person({\n id: new URL(\"https://remote.example/actors/john\"),\n preferredUsername: \"john\",\n }),\n object: new URL(\"https://example.com/ap/actor/region_kr\"),\n }),\n );\n assert.deepStrictEqual(followed, [\"region_kr\"]);\n });\n\n test(\"resolve usernames through mapUsername\", async () => {\n const { instance } = createInstance();\n instance.createBot(\n (_ctx, identifier) => regionProfile(identifier),\n {\n mapUsername: (_ctx, username) =>\n username.startsWith(\"region_\") ? username : `region_${username}`,\n },\n );\n const response = await instance.fetch(\n new Request(\n \"https://example.com/.well-known/webfinger?resource=acct:kr@example.com\",\n ),\n undefined,\n );\n assert.deepStrictEqual(response.status, 200);\n const jrd = await response.json();\n const self = jrd.links.find((link: { rel: string }) => link.rel === \"self\");\n assert.deepStrictEqual(\n self.href,\n \"https://example.com/ap/actor/region_kr\",\n );\n });\n\n test(\"fall back to username-as-identifier lookups\", async () => {\n const { instance } = createInstance();\n instance.createBot((_ctx, identifier) => regionProfile(identifier));\n const response = await instance.fetch(\n new Request(\n \"https://example.com/.well-known/webfinger?resource=acct:region_kr@example.com\",\n ),\n undefined,\n );\n assert.deepStrictEqual(response.status, 200);\n });\n\n test(\"require context data unless TContextData is void\", () => {\n const instance = new InstanceImpl<{ db: string }>({\n kv: new MemoryKvStore(),\n repository: new MemoryRepository(),\n });\n const group = instance.createBot((_ctx, identifier) => ({\n username: identifier,\n }));\n // @ts-expect-error: contextData is required when TContextData is not\n // void.\n group.getSession(\"https://example.com\", \"someone\").catch(() => {});\n group.getSession(\"https://example.com\", \"someone\", { db: \"x\" })\n .catch(() => {});\n });\n\n test(\"create sessions for resolved identifiers\", async () => {\n const { instance } = createInstance();\n const group = instance.createBot(\n (_ctx, identifier) => regionProfile(identifier),\n );\n const session = await group.getSession(\n \"https://example.com\",\n \"region_kr\",\n );\n assert.deepStrictEqual(\n session.actorId.href,\n \"https://example.com/ap/actor/region_kr\",\n );\n assert.deepStrictEqual(session.bot.identifier, \"region_kr\");\n assert.deepStrictEqual(session.bot.username, \"region_kr\");\n\n await assert.rejects(\n () => group.getSession(\"https://example.com\", \"nonexistent\"),\n TypeError,\n );\n });\n});\n\ndescribe(\"dynamic bots in routing and web pages\", () => {\n test(\"receive timeline messages via followees\", async () => {\n const { instance, repository } = createInstance();\n const group = instance.createBot(\n (_ctx, identifier) => regionProfile(identifier),\n );\n const events: string[] = [];\n group.onMessage = (session) =>\n void (events.push(`message:${session.bot.identifier}`));\n const author = new Person({\n id: new URL(\"https://example.com/ap/actor/john\"),\n preferredUsername: \"john\",\n });\n await repository.addFollowee(\n \"region_kr\",\n author.id!,\n new Follow({\n id: new URL(\n \"https://example.com/ap/actor/region_kr/follow/e35ff5d8-ede9-4f5e-9b83-4bfcd4c9a69c\",\n ),\n actor: new URL(\"https://example.com/ap/actor/region_kr\"),\n object: author.id!,\n }),\n );\n const ctx = instance.federation.createContext(\n new URL(\"https://example.com/\"),\n undefined,\n ) as InboxContext<void>;\n Object.defineProperty(ctx, \"recipient\", {\n value: null,\n configurable: true,\n });\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 timeline post\",\n }),\n }),\n );\n assert.deepStrictEqual(events, [\"message:region_kr\"]);\n });\n\n test(\"serve dynamic bots' web pages\", async () => {\n const { instance } = createInstance();\n instance.createBot((_ctx, identifier) => regionProfile(identifier));\n const response = await instance.fetch(\n new Request(\"https://example.com/@region_kr\"),\n undefined,\n );\n assert.deepStrictEqual(response.status, 200);\n const html = await response.text();\n assert.ok(html.includes(\"@region_kr@example.com\"));\n\n const miss = await instance.fetch(\n new Request(\"https://example.com/@nonexistent\"),\n undefined,\n );\n assert.deepStrictEqual(miss.status, 404);\n });\n});\n\ndescribe(\"mapUsername ownership\", () => {\n test(\"cannot map usernames to other bots' identifiers\", async () => {\n const { instance } = createInstance();\n instance.createBot(\"staticbot\", { username: \"staticbot\" });\n instance.createBot(\n (_ctx, identifier) => regionProfile(identifier),\n {\n // A hostile or buggy mapping pointing at a static bot's identifier:\n mapUsername: () => \"staticbot\",\n },\n );\n const response = await instance.fetch(\n new Request(\n \"https://example.com/.well-known/webfinger?resource=acct:hijack@example.com\",\n ),\n undefined,\n );\n assert.deepStrictEqual(response.status, 404);\n });\n});\n\ndescribe(\"username-as-identifier fallback scope\", () => {\n test(\"does not expose static bots' internal identifiers\", async () => {\n const { instance } = createInstance();\n instance.createBot(\"internal\", { username: \"mybot\" });\n const response = await instance.fetch(\n new Request(\n \"https://example.com/.well-known/webfinger?resource=acct:internal@example.com\",\n ),\n undefined,\n );\n assert.deepStrictEqual(response.status, 404);\n });\n});\n"],"mappings":"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"}
@@ -5,8 +5,10 @@ import { MessageClass } from "./message.js";
5
5
  import { Session } from "./session.js";
6
6
  import { CustomEmoji, DeferredCustomEmoji } from "./emoji.js";
7
7
  import { AcceptEventHandler, FollowEventHandler, LikeEventHandler, MentionEventHandler, MessageEventHandler, QuoteEventHandler, ReactionEventHandler, RejectEventHandler, ReplyEventHandler, SharedMessageEventHandler, UndoneReactionEventHandler, UnfollowEventHandler, UnlikeEventHandler, VoteEventHandler } from "./events.js";
8
- import { Repository } from "./repository.js";
8
+ import { ActorScopedRepository, Repository, RepositoryGetFollowersOptions, RepositoryGetMessagesOptions, Uuid } from "./repository.js";
9
9
  import { Bot, CreateBotOptions, PagesOptions } from "./bot.js";
10
+ import { InstanceImpl } from "./instance-impl.js";
11
+ import { BotDispatcher, BotGroup, BotProfile, CreateBotGroupOptions } from "./instance.js";
10
12
  import { SessionImpl } from "./session-impl.js";
11
13
  import { Accept, Activity, Actor, Announce, Application, Create, Emoji, EmojiReact, Follow, Image, Like, Link, Object as Object$1, PropertyValue, Recipient, Reject, Service, Undo } from "@fedify/vocab";
12
14
  import { Context, Federation, InboxContext, NodeInfo, PageItems, RequestContext, Software, UnverifiedActivityReason } from "@fedify/fedify";
@@ -14,7 +16,23 @@ import { Context, Federation, InboxContext, NodeInfo, PageItems, RequestContext,
14
16
  //#region src/bot-impl.d.ts
15
17
  interface BotImplOptions<TContextData> extends CreateBotOptions<TContextData> {
16
18
  collectionWindow?: number;
19
+ /**
20
+ * The instance to host the bot on. If omitted, a dedicated instance is
21
+ * created from the given options, which preserves the single-bot behavior
22
+ * of BotKit 0.4 and earlier.
23
+ */
24
+ instance?: InstanceImpl<TContextData>;
25
+ /**
26
+ * Whether the bot is a transient view of a dynamically resolved bot,
27
+ * which is not registered on the instance.
28
+ */
29
+ transient?: boolean;
17
30
  }
31
+ /**
32
+ * The names of the event handler properties of {@link BotEventHandlers}.
33
+ * @internal
34
+ */
35
+ declare const botEventHandlerNames: readonly ["onFollow", "onUnfollow", "onAcceptFollow", "onRejectFollow", "onMention", "onReply", "onQuote", "onMessage", "onSharedMessage", "onLike", "onUnlike", "onReact", "onUnreact", "onVote"];
18
36
  declare class BotImpl<TContextData> implements Bot<TContextData> {
19
37
  #private;
20
38
  readonly identifier: string;
@@ -26,13 +44,26 @@ declare class BotImpl<TContextData> implements Bot<TContextData> {
26
44
  readonly image?: URL | Image;
27
45
  readonly properties: Record<string, Text<"block" | "inline", TContextData>>;
28
46
  readonly followerPolicy: "accept" | "reject" | "manual";
29
- readonly customEmojis: Record<string, CustomEmoji>;
30
- readonly repository: Repository;
31
- readonly software?: Software;
32
- readonly behindProxy: boolean;
33
- readonly pages: Required<PagesOptions>;
34
- readonly collectionWindow: number;
35
- readonly federation: Federation<TContextData>;
47
+ readonly repository: ActorScopedRepository;
48
+ /**
49
+ * The instance hosting the bot. It owns the shared infrastructure:
50
+ * the Fedify federation, the key–value store, the message queue,
51
+ * the root repository, and HTTP handling.
52
+ */
53
+ readonly instance: InstanceImpl<TContextData>;
54
+ get customEmojis(): Record<string, CustomEmoji>;
55
+ get software(): Software | undefined;
56
+ get behindProxy(): boolean;
57
+ get pages(): Required<PagesOptions>;
58
+ get collectionWindow(): number;
59
+ get federation(): Federation<TContextData>;
60
+ /**
61
+ * The identifier of the bot actor that owns local objects whose URIs are
62
+ * in the legacy (pre-0.5) format, which did not carry the identifier.
63
+ * Legacy URIs can only occur in deployments that hosted a single bot
64
+ * before the upgrade, so they are attributed to that bot.
65
+ */
66
+ get legacyObjectUrisIdentifier(): string | undefined;
36
67
  onFollow?: FollowEventHandler<TContextData>;
37
68
  onUnfollow?: UnfollowEventHandler<TContextData>;
38
69
  onAcceptFollow?: AcceptEventHandler<TContextData>;
@@ -48,7 +79,6 @@ declare class BotImpl<TContextData> implements Bot<TContextData> {
48
79
  onUnreact?: UndoneReactionEventHandler<TContextData>;
49
80
  onVote?: VoteEventHandler<TContextData>;
50
81
  constructor(options: BotImplOptions<TContextData>);
51
- initialize(): void;
52
82
  getActorSummary(session: Session<TContextData>): Promise<{
53
83
  text: string;
54
84
  tags: (Link | Object$1)[];
@@ -68,25 +98,29 @@ declare class BotImpl<TContextData> implements Bot<TContextData> {
68
98
  getOutboxFirstCursor(_ctx: Context<TContextData>, identifier: string): string | null;
69
99
  countOutbox(_ctx: Context<TContextData>, identifier: string): Promise<number | null>;
70
100
  dispatchFollow(_ctx: RequestContext<TContextData>, values: {
101
+ identifier: string;
71
102
  id: string;
72
103
  }): Promise<Follow | null>;
73
104
  authorizeFollow(ctx: RequestContext<TContextData>, values: {
105
+ identifier: string;
74
106
  id: string;
75
107
  }): Promise<boolean>;
76
108
  dispatchCreate(ctx: RequestContext<TContextData>, values: {
109
+ identifier: string;
77
110
  id: string;
78
111
  }): Promise<Create | null>;
79
112
  dispatchMessage<T extends MessageClass>(cls: new (values: any) => T, ctx: Context<TContextData> | RequestContext<TContextData>, id: string): Promise<T | null>;
80
113
  dispatchAnnounce(ctx: RequestContext<TContextData>, values: {
114
+ identifier: string;
81
115
  id: string;
82
116
  }): Promise<Announce | null>;
83
117
  dispatchEmoji(ctx: Context<TContextData>, values: {
84
118
  name: string;
85
119
  }): Emoji | null;
86
- dispatchSharedKey(_ctx: Context<TContextData>): {
120
+ dispatchSharedKey(ctx: Context<TContextData>): {
87
121
  identifier: string;
88
122
  };
89
- onUnverifiedActivity(_ctx: RequestContext<TContextData>, activity: Activity, reason: UnverifiedActivityReason): Response | void;
123
+ onUnverifiedActivity(ctx: RequestContext<TContextData>, activity: Activity, reason: UnverifiedActivityReason): Response | void;
90
124
  onFollowed(ctx: InboxContext<TContextData>, follow: Follow): Promise<void>;
91
125
  onUnfollowed(ctx: InboxContext<TContextData>, undo: Undo): Promise<void>;
92
126
  onFollowAccepted(ctx: InboxContext<TContextData>, accept: Accept): Promise<void>;
@@ -97,7 +131,7 @@ declare class BotImpl<TContextData> implements Bot<TContextData> {
97
131
  onUnliked(ctx: InboxContext<TContextData>, undo: Undo): Promise<void>;
98
132
  onReacted(ctx: InboxContext<TContextData>, react: EmojiReact | Like): Promise<void>;
99
133
  onUnreacted(ctx: InboxContext<TContextData>, undo: Undo): Promise<void>;
100
- dispatchNodeInfo(_ctx: Context<TContextData>): NodeInfo;
134
+ dispatchNodeInfo(ctx: Context<TContextData>): NodeInfo;
101
135
  getSession(origin: string | URL, contextData: TContextData): SessionImpl<TContextData>;
102
136
  getSession(origin: string | URL): SessionImpl<TContextData>;
103
137
  getSession(context: Context<TContextData>): SessionImpl<TContextData>;
@@ -107,8 +141,93 @@ declare class BotImpl<TContextData> implements Bot<TContextData> {
107
141
  addCustomEmoji<TEmojiName extends string>(name: TEmojiName, data: CustomEmoji): DeferredCustomEmoji<TContextData>;
108
142
  addCustomEmojis<TEmojiName extends string>(emojis: Readonly<Record<TEmojiName, CustomEmoji>>): Readonly<Record<TEmojiName, DeferredCustomEmoji<TContextData>>>;
109
143
  }
144
+ /**
145
+ * Wraps a {@link BotImpl} instance with a plain object implementing
146
+ * the {@link Bot} interface. Since `deno serve` does not recognize a class
147
+ * instance having fetch(), we wrap a BotImpl instance with a plain object.
148
+ * See also https://github.com/denoland/deno/issues/24062
149
+ * @param bot The bot implementation to wrap.
150
+ * @returns The wrapped bot.
151
+ * @internal
152
+ */
153
+ declare function wrapBotImpl<TContextData>(bot: BotImpl<TContextData>): Bot<TContextData>;
154
+ /**
155
+ * A repository decorator that adopts legacy (pre-0.5) data for a bot actor
156
+ * before the first repository operation. The migration is kicked off at
157
+ * construction time and every operation awaits its completion, so data
158
+ * stored by BotKit 0.4 or earlier is visible from the start.
159
+ * @internal
160
+ */
161
+ declare class MigrationGatedRepository implements Repository {
162
+ #private;
163
+ constructor(repository: Repository, identifier: string);
164
+ setKeyPairs(identifier: string, keyPairs: CryptoKeyPair[]): Promise<void>;
165
+ getKeyPairs(identifier: string): Promise<CryptoKeyPair[] | undefined>;
166
+ addMessage(identifier: string, id: Uuid, activity: Create | Announce): Promise<void>;
167
+ updateMessage(identifier: string, id: Uuid, updater: (existing: Create | Announce) => Create | Announce | undefined | Promise<Create | Announce | undefined>): Promise<boolean>;
168
+ removeMessage(identifier: string, id: Uuid): Promise<Create | Announce | undefined>;
169
+ getMessages(identifier: string, options?: RepositoryGetMessagesOptions): AsyncIterable<Create | Announce>;
170
+ getMessage(identifier: string, id: Uuid): Promise<Create | Announce | undefined>;
171
+ countMessages(identifier: string): Promise<number>;
172
+ addFollower(identifier: string, followId: URL, follower: Actor): Promise<void>;
173
+ removeFollower(identifier: string, followId: URL, followerId: URL): Promise<Actor | undefined>;
174
+ hasFollower(identifier: string, followerId: URL): Promise<boolean>;
175
+ getFollowers(identifier: string, options?: RepositoryGetFollowersOptions): AsyncIterable<Actor>;
176
+ countFollowers(identifier: string): Promise<number>;
177
+ addSentFollow(identifier: string, id: Uuid, follow: Follow): Promise<void>;
178
+ removeSentFollow(identifier: string, id: Uuid): Promise<Follow | undefined>;
179
+ getSentFollow(identifier: string, id: Uuid): Promise<Follow | undefined>;
180
+ addFollowee(identifier: string, followeeId: URL, follow: Follow): Promise<void>;
181
+ removeFollowee(identifier: string, followeeId: URL): Promise<Follow | undefined>;
182
+ getFollowee(identifier: string, followeeId: URL): Promise<Follow | undefined>;
183
+ findFollowedBots(followeeId: URL): AsyncIterable<string>;
184
+ vote(identifier: string, messageId: Uuid, voterId: URL, option: string): Promise<void>;
185
+ countVoters(identifier: string, messageId: Uuid): Promise<number>;
186
+ countVotes(identifier: string, messageId: Uuid): Promise<Readonly<Record<string, number>>>;
187
+ forIdentifier(identifier: string): ActorScopedRepository;
188
+ migrate(identifier: string): Promise<void>;
189
+ }
190
+ /**
191
+ * The internal implementation of a {@link BotGroup}: a registry of event
192
+ * handlers shared by every bot its dispatcher resolves.
193
+ * @internal
194
+ */
195
+ declare class BotGroupImpl<TContextData> implements BotGroup<TContextData> {
196
+ readonly instance: InstanceImpl<TContextData>;
197
+ readonly dispatcher: BotDispatcher<TContextData>;
198
+ readonly mapUsername?: (ctx: Context<TContextData>, username: string) => string | null | Promise<string | null>;
199
+ onFollow?: FollowEventHandler<TContextData>;
200
+ onUnfollow?: UnfollowEventHandler<TContextData>;
201
+ onAcceptFollow?: AcceptEventHandler<TContextData>;
202
+ onRejectFollow?: RejectEventHandler<TContextData>;
203
+ onMention?: MentionEventHandler<TContextData>;
204
+ onReply?: ReplyEventHandler<TContextData>;
205
+ onQuote?: QuoteEventHandler<TContextData>;
206
+ onMessage?: MessageEventHandler<TContextData>;
207
+ onSharedMessage?: SharedMessageEventHandler<TContextData>;
208
+ onLike?: LikeEventHandler<TContextData>;
209
+ onUnlike?: UnlikeEventHandler<TContextData>;
210
+ onReact?: ReactionEventHandler<TContextData>;
211
+ onUnreact?: UndoneReactionEventHandler<TContextData>;
212
+ onVote?: VoteEventHandler<TContextData>;
213
+ constructor(instance: InstanceImpl<TContextData>, dispatcher: BotDispatcher<TContextData>, options?: CreateBotGroupOptions<TContextData>);
214
+ getSession(origin: string | URL, identifier: string, contextData: TContextData): Promise<Session<TContextData>>;
215
+ }
216
+ /**
217
+ * A transient per-bot view of a dynamically resolved bot. It behaves like
218
+ * a regular {@link BotImpl}, except that its event handlers are read live
219
+ * from the owning {@link BotGroupImpl} at dispatch time, so handlers
220
+ * registered on the group after a bot was resolved still fire. Views are
221
+ * not registered on the instance and live only as long as the resolution
222
+ * cache of the request that produced them.
223
+ * @internal
224
+ */
225
+ declare class GroupBotImpl<TContextData> extends BotImpl<TContextData> {
226
+ readonly group: BotGroupImpl<TContextData>;
227
+ constructor(group: BotGroupImpl<TContextData>, identifier: string, profile: BotProfile<TContextData>);
228
+ }
110
229
  //# sourceMappingURL=bot-impl.d.ts.map
111
230
 
112
231
  //#endregion
113
- export { BotImpl, BotImplOptions };
232
+ export { BotGroupImpl, BotImpl, BotImplOptions, GroupBotImpl, MigrationGatedRepository, botEventHandlerNames, wrapBotImpl };
114
233
  //# sourceMappingURL=bot-impl.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bot-impl.d.ts","names":[],"sources":["../src/bot-impl.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"bot-impl.d.ts","names":[],"sources":["../src/bot-impl.ts"],"sourcesContent":[],"mappings":"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"}