@fedify/botkit-sqlite 0.5.0-dev.209 → 0.5.0-dev.225
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mod.d.ts +48 -23
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +342 -109
- package/dist/mod.js.map +1 -1
- package/package.json +2 -2
package/dist/mod.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Temporal, toTemporalInstant } from "@js-temporal/polyfill";
|
|
2
2
|
Date.prototype.toTemporalInstant = toTemporalInstant;
|
|
3
|
+
import { ActorScopedRepository, Repository, RepositoryGetFollowersOptions, RepositoryGetMessagesOptions, Uuid } from "@fedify/botkit/repository";
|
|
3
4
|
import { Actor, Announce, Create, Follow } from "@fedify/vocab";
|
|
4
|
-
import { Repository, RepositoryGetFollowersOptions, RepositoryGetMessagesOptions, Uuid } from "@fedify/botkit/repository";
|
|
5
5
|
|
|
6
6
|
//#region src/mod.d.ts
|
|
7
7
|
/**
|
|
@@ -36,29 +36,54 @@ declare class SqliteRepository implements Repository, Disposable {
|
|
|
36
36
|
* Closes the database connection.
|
|
37
37
|
*/
|
|
38
38
|
close(): void;
|
|
39
|
+
private tableExists;
|
|
40
|
+
private hasBotIdColumn;
|
|
41
|
+
/**
|
|
42
|
+
* Rebuilds tables created by \@fedify/botkit-sqlite 0.4 or earlier, which
|
|
43
|
+
* had no `bot_id` column, into the bot-scoped schema. Existing rows get
|
|
44
|
+
* the empty-string bot ID; use {@link SqliteRepository.migrate} to assign
|
|
45
|
+
* them to a bot actor identifier.
|
|
46
|
+
*/
|
|
47
|
+
private rebuildLegacyTables;
|
|
48
|
+
/**
|
|
49
|
+
* Migrates data stored by \@fedify/botkit-sqlite 0.4 or earlier, which was
|
|
50
|
+
* not scoped by bot actor identifiers, so that it belongs to the given
|
|
51
|
+
* identifier. Rows carried over from a legacy database have the
|
|
52
|
+
* empty-string bot ID; this method assigns them to the identifier in
|
|
53
|
+
* a single transaction. It only acts when the database was actually
|
|
54
|
+
* rebuilt from a legacy schema, so data legitimately stored under an
|
|
55
|
+
* empty-string identifier is never touched, and calling it again is
|
|
56
|
+
* a no-op.
|
|
57
|
+
* @param identifier The identifier of the bot actor that adopts the legacy
|
|
58
|
+
* data.
|
|
59
|
+
* @since 0.5.0
|
|
60
|
+
*/
|
|
61
|
+
migrate(identifier: string): Promise<void>;
|
|
39
62
|
private initializeTables;
|
|
40
|
-
setKeyPairs(keyPairs: CryptoKeyPair[]): Promise<void>;
|
|
41
|
-
getKeyPairs(): Promise<CryptoKeyPair[] | undefined>;
|
|
42
|
-
addMessage(id: Uuid, activity: Create | Announce): Promise<void>;
|
|
43
|
-
updateMessage(id: Uuid, updater: (existing: Create | Announce) => Create | Announce | undefined | Promise<Create | Announce | undefined>): Promise<boolean>;
|
|
44
|
-
removeMessage(id: Uuid): Promise<Create | Announce | undefined>;
|
|
45
|
-
getMessages(options?: RepositoryGetMessagesOptions): AsyncIterable<Create | Announce>;
|
|
46
|
-
getMessage(id: Uuid): Promise<Create | Announce | undefined>;
|
|
47
|
-
countMessages(): Promise<number>;
|
|
48
|
-
addFollower(followRequestId: URL, follower: Actor): Promise<void>;
|
|
49
|
-
removeFollower(followRequestId: URL, actorId: URL): Promise<Actor | undefined>;
|
|
50
|
-
hasFollower(followerId: URL): Promise<boolean>;
|
|
51
|
-
getFollowers(options?: RepositoryGetFollowersOptions): AsyncIterable<Actor>;
|
|
52
|
-
countFollowers(): Promise<number>;
|
|
53
|
-
addSentFollow(id: Uuid, follow: Follow): Promise<void>;
|
|
54
|
-
removeSentFollow(id: Uuid): Promise<Follow | undefined>;
|
|
55
|
-
getSentFollow(id: Uuid): Promise<Follow | undefined>;
|
|
56
|
-
addFollowee(followeeId: URL, follow: Follow): Promise<void>;
|
|
57
|
-
removeFollowee(followeeId: URL): Promise<Follow | undefined>;
|
|
58
|
-
getFollowee(followeeId: URL): Promise<Follow | undefined>;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
setKeyPairs(identifier: string, keyPairs: CryptoKeyPair[]): Promise<void>;
|
|
64
|
+
getKeyPairs(identifier: string): Promise<CryptoKeyPair[] | undefined>;
|
|
65
|
+
addMessage(identifier: string, id: Uuid, activity: Create | Announce): Promise<void>;
|
|
66
|
+
updateMessage(identifier: string, id: Uuid, updater: (existing: Create | Announce) => Create | Announce | undefined | Promise<Create | Announce | undefined>): Promise<boolean>;
|
|
67
|
+
removeMessage(identifier: string, id: Uuid): Promise<Create | Announce | undefined>;
|
|
68
|
+
getMessages(identifier: string, options?: RepositoryGetMessagesOptions): AsyncIterable<Create | Announce>;
|
|
69
|
+
getMessage(identifier: string, id: Uuid): Promise<Create | Announce | undefined>;
|
|
70
|
+
countMessages(identifier: string): Promise<number>;
|
|
71
|
+
addFollower(identifier: string, followRequestId: URL, follower: Actor): Promise<void>;
|
|
72
|
+
removeFollower(identifier: string, followRequestId: URL, actorId: URL): Promise<Actor | undefined>;
|
|
73
|
+
hasFollower(identifier: string, followerId: URL): Promise<boolean>;
|
|
74
|
+
getFollowers(identifier: string, options?: RepositoryGetFollowersOptions): AsyncIterable<Actor>;
|
|
75
|
+
countFollowers(identifier: string): Promise<number>;
|
|
76
|
+
addSentFollow(identifier: string, id: Uuid, follow: Follow): Promise<void>;
|
|
77
|
+
removeSentFollow(identifier: string, id: Uuid): Promise<Follow | undefined>;
|
|
78
|
+
getSentFollow(identifier: string, id: Uuid): Promise<Follow | undefined>;
|
|
79
|
+
addFollowee(identifier: string, followeeId: URL, follow: Follow): Promise<void>;
|
|
80
|
+
removeFollowee(identifier: string, followeeId: URL): Promise<Follow | undefined>;
|
|
81
|
+
getFollowee(identifier: string, followeeId: URL): Promise<Follow | undefined>;
|
|
82
|
+
findFollowedBots(followeeId: URL): AsyncIterable<string>;
|
|
83
|
+
vote(identifier: string, messageId: Uuid, voterId: URL, option: string): Promise<void>;
|
|
84
|
+
countVoters(identifier: string, messageId: Uuid): Promise<number>;
|
|
85
|
+
countVotes(identifier: string, messageId: Uuid): Promise<Readonly<Record<string, number>>>;
|
|
86
|
+
forIdentifier(identifier: string): ActorScopedRepository;
|
|
62
87
|
}
|
|
63
88
|
//# sourceMappingURL=mod.d.ts.map
|
|
64
89
|
|
package/dist/mod.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.ts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"mod.d.ts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/mod.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Temporal, toTemporalInstant } from "@js-temporal/polyfill";
|
|
3
3
|
Date.prototype.toTemporalInstant = toTemporalInstant;
|
|
4
4
|
|
|
5
|
+
import { ActorScopedRepository } from "@fedify/botkit/repository";
|
|
5
6
|
import { exportJwk, importJwk } from "@fedify/fedify/sig";
|
|
6
7
|
import { Activity, Announce, Create, Follow, Object as Object$1, isActor } from "@fedify/vocab";
|
|
7
8
|
import { getLogger } from "@logtape/logtape";
|
|
@@ -35,75 +36,294 @@ var SqliteRepository = class {
|
|
|
35
36
|
close() {
|
|
36
37
|
this.db.close();
|
|
37
38
|
}
|
|
39
|
+
tableExists(table) {
|
|
40
|
+
const stmt = this.db.prepare(`
|
|
41
|
+
SELECT COUNT(*) AS count FROM sqlite_master
|
|
42
|
+
WHERE type = 'table' AND name = ?
|
|
43
|
+
`);
|
|
44
|
+
const row = stmt.get(table);
|
|
45
|
+
return row.count > 0;
|
|
46
|
+
}
|
|
47
|
+
hasBotIdColumn(table) {
|
|
48
|
+
const stmt = this.db.prepare(`
|
|
49
|
+
SELECT COUNT(*) AS count FROM pragma_table_info(?)
|
|
50
|
+
WHERE name = 'bot_id'
|
|
51
|
+
`);
|
|
52
|
+
const row = stmt.get(table);
|
|
53
|
+
return row.count > 0;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Rebuilds tables created by \@fedify/botkit-sqlite 0.4 or earlier, which
|
|
57
|
+
* had no `bot_id` column, into the bot-scoped schema. Existing rows get
|
|
58
|
+
* the empty-string bot ID; use {@link SqliteRepository.migrate} to assign
|
|
59
|
+
* them to a bot actor identifier.
|
|
60
|
+
*/
|
|
61
|
+
rebuildLegacyTables() {
|
|
62
|
+
const tables = [
|
|
63
|
+
"key_pairs",
|
|
64
|
+
"messages",
|
|
65
|
+
"followers",
|
|
66
|
+
"follow_requests",
|
|
67
|
+
"sent_follows",
|
|
68
|
+
"followees",
|
|
69
|
+
"poll_votes"
|
|
70
|
+
].filter((table) => this.tableExists(table) && !this.hasBotIdColumn(table));
|
|
71
|
+
if (tables.length < 1) return;
|
|
72
|
+
logger.info("Rebuilding legacy tables without a bot_id column: {tables}.", { tables });
|
|
73
|
+
this.db.exec(`
|
|
74
|
+
CREATE TABLE IF NOT EXISTS botkit_metadata (
|
|
75
|
+
key TEXT PRIMARY KEY,
|
|
76
|
+
value TEXT NOT NULL
|
|
77
|
+
)
|
|
78
|
+
`);
|
|
79
|
+
this.db.exec("PRAGMA foreign_keys = OFF;");
|
|
80
|
+
this.db.exec("BEGIN TRANSACTION");
|
|
81
|
+
try {
|
|
82
|
+
if (tables.includes("key_pairs")) this.db.exec(`
|
|
83
|
+
ALTER TABLE key_pairs ADD COLUMN bot_id TEXT NOT NULL DEFAULT ''
|
|
84
|
+
`);
|
|
85
|
+
if (tables.includes("messages")) {
|
|
86
|
+
this.db.exec(`
|
|
87
|
+
CREATE TABLE messages_new (
|
|
88
|
+
bot_id TEXT NOT NULL,
|
|
89
|
+
id TEXT NOT NULL,
|
|
90
|
+
activity_json TEXT NOT NULL,
|
|
91
|
+
published INTEGER,
|
|
92
|
+
PRIMARY KEY (bot_id, id)
|
|
93
|
+
)
|
|
94
|
+
`);
|
|
95
|
+
this.db.exec(`
|
|
96
|
+
INSERT INTO messages_new (bot_id, id, activity_json, published)
|
|
97
|
+
SELECT '', id, activity_json, published FROM messages
|
|
98
|
+
`);
|
|
99
|
+
this.db.exec("DROP TABLE messages");
|
|
100
|
+
this.db.exec("ALTER TABLE messages_new RENAME TO messages");
|
|
101
|
+
}
|
|
102
|
+
if (tables.includes("followers")) {
|
|
103
|
+
this.db.exec(`
|
|
104
|
+
CREATE TABLE followers_new (
|
|
105
|
+
bot_id TEXT NOT NULL,
|
|
106
|
+
follower_id TEXT NOT NULL,
|
|
107
|
+
actor_json TEXT NOT NULL,
|
|
108
|
+
PRIMARY KEY (bot_id, follower_id)
|
|
109
|
+
)
|
|
110
|
+
`);
|
|
111
|
+
this.db.exec(`
|
|
112
|
+
INSERT INTO followers_new (bot_id, follower_id, actor_json)
|
|
113
|
+
SELECT '', follower_id, actor_json FROM followers
|
|
114
|
+
`);
|
|
115
|
+
this.db.exec("DROP TABLE followers");
|
|
116
|
+
this.db.exec("ALTER TABLE followers_new RENAME TO followers");
|
|
117
|
+
}
|
|
118
|
+
if (tables.includes("follow_requests")) {
|
|
119
|
+
this.db.exec(`
|
|
120
|
+
CREATE TABLE follow_requests_new (
|
|
121
|
+
bot_id TEXT NOT NULL,
|
|
122
|
+
follow_request_id TEXT NOT NULL,
|
|
123
|
+
follower_id TEXT NOT NULL,
|
|
124
|
+
PRIMARY KEY (bot_id, follow_request_id),
|
|
125
|
+
FOREIGN KEY (bot_id, follower_id)
|
|
126
|
+
REFERENCES followers(bot_id, follower_id)
|
|
127
|
+
)
|
|
128
|
+
`);
|
|
129
|
+
this.db.exec(`
|
|
130
|
+
INSERT INTO follow_requests_new
|
|
131
|
+
(bot_id, follow_request_id, follower_id)
|
|
132
|
+
SELECT '', follow_request_id, follower_id FROM follow_requests
|
|
133
|
+
`);
|
|
134
|
+
this.db.exec("DROP TABLE follow_requests");
|
|
135
|
+
this.db.exec("ALTER TABLE follow_requests_new RENAME TO follow_requests");
|
|
136
|
+
}
|
|
137
|
+
if (tables.includes("sent_follows")) {
|
|
138
|
+
this.db.exec(`
|
|
139
|
+
CREATE TABLE sent_follows_new (
|
|
140
|
+
bot_id TEXT NOT NULL,
|
|
141
|
+
id TEXT NOT NULL,
|
|
142
|
+
follow_json TEXT NOT NULL,
|
|
143
|
+
PRIMARY KEY (bot_id, id)
|
|
144
|
+
)
|
|
145
|
+
`);
|
|
146
|
+
this.db.exec(`
|
|
147
|
+
INSERT INTO sent_follows_new (bot_id, id, follow_json)
|
|
148
|
+
SELECT '', id, follow_json FROM sent_follows
|
|
149
|
+
`);
|
|
150
|
+
this.db.exec("DROP TABLE sent_follows");
|
|
151
|
+
this.db.exec("ALTER TABLE sent_follows_new RENAME TO sent_follows");
|
|
152
|
+
}
|
|
153
|
+
if (tables.includes("followees")) {
|
|
154
|
+
this.db.exec(`
|
|
155
|
+
CREATE TABLE followees_new (
|
|
156
|
+
bot_id TEXT NOT NULL,
|
|
157
|
+
followee_id TEXT NOT NULL,
|
|
158
|
+
follow_json TEXT NOT NULL,
|
|
159
|
+
PRIMARY KEY (bot_id, followee_id)
|
|
160
|
+
)
|
|
161
|
+
`);
|
|
162
|
+
this.db.exec(`
|
|
163
|
+
INSERT INTO followees_new (bot_id, followee_id, follow_json)
|
|
164
|
+
SELECT '', followee_id, follow_json FROM followees
|
|
165
|
+
`);
|
|
166
|
+
this.db.exec("DROP TABLE followees");
|
|
167
|
+
this.db.exec("ALTER TABLE followees_new RENAME TO followees");
|
|
168
|
+
}
|
|
169
|
+
if (tables.includes("poll_votes")) {
|
|
170
|
+
this.db.exec(`
|
|
171
|
+
CREATE TABLE poll_votes_new (
|
|
172
|
+
bot_id TEXT NOT NULL,
|
|
173
|
+
message_id TEXT NOT NULL,
|
|
174
|
+
voter_id TEXT NOT NULL,
|
|
175
|
+
option TEXT NOT NULL,
|
|
176
|
+
PRIMARY KEY (bot_id, message_id, voter_id, option)
|
|
177
|
+
)
|
|
178
|
+
`);
|
|
179
|
+
this.db.exec(`
|
|
180
|
+
INSERT INTO poll_votes_new (bot_id, message_id, voter_id, option)
|
|
181
|
+
SELECT '', message_id, voter_id, option FROM poll_votes
|
|
182
|
+
`);
|
|
183
|
+
this.db.exec("DROP TABLE poll_votes");
|
|
184
|
+
this.db.exec("ALTER TABLE poll_votes_new RENAME TO poll_votes");
|
|
185
|
+
}
|
|
186
|
+
this.db.exec(`
|
|
187
|
+
INSERT OR REPLACE INTO botkit_metadata (key, value)
|
|
188
|
+
VALUES ('legacy_data', '1')
|
|
189
|
+
`);
|
|
190
|
+
this.db.exec("COMMIT");
|
|
191
|
+
} catch (error) {
|
|
192
|
+
this.db.exec("ROLLBACK");
|
|
193
|
+
this.db.exec("PRAGMA foreign_keys = ON;");
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
this.db.exec("PRAGMA foreign_keys = ON;");
|
|
197
|
+
logger.info("Finished rebuilding legacy tables.");
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Migrates data stored by \@fedify/botkit-sqlite 0.4 or earlier, which was
|
|
201
|
+
* not scoped by bot actor identifiers, so that it belongs to the given
|
|
202
|
+
* identifier. Rows carried over from a legacy database have the
|
|
203
|
+
* empty-string bot ID; this method assigns them to the identifier in
|
|
204
|
+
* a single transaction. It only acts when the database was actually
|
|
205
|
+
* rebuilt from a legacy schema, so data legitimately stored under an
|
|
206
|
+
* empty-string identifier is never touched, and calling it again is
|
|
207
|
+
* a no-op.
|
|
208
|
+
* @param identifier The identifier of the bot actor that adopts the legacy
|
|
209
|
+
* data.
|
|
210
|
+
* @since 0.5.0
|
|
211
|
+
*/
|
|
212
|
+
migrate(identifier) {
|
|
213
|
+
if (!this.tableExists("botkit_metadata")) return Promise.resolve();
|
|
214
|
+
const marker = this.db.prepare("SELECT value FROM botkit_metadata WHERE key = 'legacy_data'").get();
|
|
215
|
+
if (marker == null) return Promise.resolve();
|
|
216
|
+
this.db.exec("BEGIN TRANSACTION");
|
|
217
|
+
this.db.exec("PRAGMA defer_foreign_keys = ON");
|
|
218
|
+
try {
|
|
219
|
+
for (const table of [
|
|
220
|
+
"key_pairs",
|
|
221
|
+
"messages",
|
|
222
|
+
"followers",
|
|
223
|
+
"follow_requests",
|
|
224
|
+
"sent_follows",
|
|
225
|
+
"followees",
|
|
226
|
+
"poll_votes"
|
|
227
|
+
]) this.db.prepare(`UPDATE ${table} SET bot_id = ? WHERE bot_id = ''`).run(identifier);
|
|
228
|
+
this.db.exec("DELETE FROM botkit_metadata WHERE key = 'legacy_data'");
|
|
229
|
+
this.db.exec("COMMIT");
|
|
230
|
+
} catch (error) {
|
|
231
|
+
this.db.exec("ROLLBACK");
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
return Promise.resolve();
|
|
235
|
+
}
|
|
38
236
|
initializeTables() {
|
|
237
|
+
this.rebuildLegacyTables();
|
|
39
238
|
this.db.exec(`
|
|
40
239
|
CREATE TABLE IF NOT EXISTS key_pairs (
|
|
41
240
|
id INTEGER PRIMARY KEY,
|
|
241
|
+
bot_id TEXT NOT NULL,
|
|
42
242
|
private_key_jwk TEXT NOT NULL,
|
|
43
243
|
public_key_jwk TEXT NOT NULL
|
|
44
244
|
)
|
|
245
|
+
`);
|
|
246
|
+
this.db.exec(`
|
|
247
|
+
CREATE INDEX IF NOT EXISTS idx_key_pairs_bot_id ON key_pairs(bot_id)
|
|
45
248
|
`);
|
|
46
249
|
this.db.exec(`
|
|
47
250
|
CREATE TABLE IF NOT EXISTS messages (
|
|
48
|
-
|
|
251
|
+
bot_id TEXT NOT NULL,
|
|
252
|
+
id TEXT NOT NULL,
|
|
49
253
|
activity_json TEXT NOT NULL,
|
|
50
|
-
published INTEGER
|
|
254
|
+
published INTEGER,
|
|
255
|
+
PRIMARY KEY (bot_id, id)
|
|
51
256
|
)
|
|
52
257
|
`);
|
|
53
258
|
this.db.exec(`
|
|
54
|
-
CREATE INDEX IF NOT EXISTS
|
|
259
|
+
CREATE INDEX IF NOT EXISTS idx_messages_bot_published
|
|
260
|
+
ON messages(bot_id, published)
|
|
55
261
|
`);
|
|
56
262
|
this.db.exec(`
|
|
57
263
|
CREATE TABLE IF NOT EXISTS followers (
|
|
58
|
-
|
|
59
|
-
|
|
264
|
+
bot_id TEXT NOT NULL,
|
|
265
|
+
follower_id TEXT NOT NULL,
|
|
266
|
+
actor_json TEXT NOT NULL,
|
|
267
|
+
PRIMARY KEY (bot_id, follower_id)
|
|
60
268
|
)
|
|
61
269
|
`);
|
|
62
270
|
this.db.exec(`
|
|
63
271
|
CREATE TABLE IF NOT EXISTS follow_requests (
|
|
64
|
-
|
|
272
|
+
bot_id TEXT NOT NULL,
|
|
273
|
+
follow_request_id TEXT NOT NULL,
|
|
65
274
|
follower_id TEXT NOT NULL,
|
|
66
|
-
|
|
275
|
+
PRIMARY KEY (bot_id, follow_request_id),
|
|
276
|
+
FOREIGN KEY (bot_id, follower_id)
|
|
277
|
+
REFERENCES followers(bot_id, follower_id)
|
|
67
278
|
)
|
|
68
279
|
`);
|
|
69
280
|
this.db.exec(`
|
|
70
281
|
CREATE TABLE IF NOT EXISTS sent_follows (
|
|
71
|
-
|
|
72
|
-
|
|
282
|
+
bot_id TEXT NOT NULL,
|
|
283
|
+
id TEXT NOT NULL,
|
|
284
|
+
follow_json TEXT NOT NULL,
|
|
285
|
+
PRIMARY KEY (bot_id, id)
|
|
73
286
|
)
|
|
74
287
|
`);
|
|
75
288
|
this.db.exec(`
|
|
76
289
|
CREATE TABLE IF NOT EXISTS followees (
|
|
77
|
-
|
|
78
|
-
|
|
290
|
+
bot_id TEXT NOT NULL,
|
|
291
|
+
followee_id TEXT NOT NULL,
|
|
292
|
+
follow_json TEXT NOT NULL,
|
|
293
|
+
PRIMARY KEY (bot_id, followee_id)
|
|
79
294
|
)
|
|
295
|
+
`);
|
|
296
|
+
this.db.exec(`
|
|
297
|
+
CREATE INDEX IF NOT EXISTS idx_followees_followee_id
|
|
298
|
+
ON followees(followee_id)
|
|
80
299
|
`);
|
|
81
300
|
this.db.exec(`
|
|
82
301
|
CREATE TABLE IF NOT EXISTS poll_votes (
|
|
302
|
+
bot_id TEXT NOT NULL,
|
|
83
303
|
message_id TEXT NOT NULL,
|
|
84
304
|
voter_id TEXT NOT NULL,
|
|
85
305
|
option TEXT NOT NULL,
|
|
86
|
-
PRIMARY KEY (message_id, voter_id, option)
|
|
306
|
+
PRIMARY KEY (bot_id, message_id, voter_id, option)
|
|
87
307
|
)
|
|
88
308
|
`);
|
|
89
309
|
this.db.exec(`
|
|
90
|
-
CREATE INDEX IF NOT EXISTS
|
|
91
|
-
ON poll_votes(message_id, option)
|
|
310
|
+
CREATE INDEX IF NOT EXISTS idx_poll_votes_bot_message_option
|
|
311
|
+
ON poll_votes(bot_id, message_id, option)
|
|
92
312
|
`);
|
|
93
313
|
}
|
|
94
|
-
async setKeyPairs(keyPairs) {
|
|
95
|
-
const deleteStmt = this.db.prepare("DELETE FROM key_pairs");
|
|
314
|
+
async setKeyPairs(identifier, keyPairs) {
|
|
315
|
+
const deleteStmt = this.db.prepare("DELETE FROM key_pairs WHERE bot_id = ?");
|
|
96
316
|
const insertStmt = this.db.prepare(`
|
|
97
|
-
INSERT INTO key_pairs (private_key_jwk, public_key_jwk)
|
|
98
|
-
VALUES (?, ?)
|
|
317
|
+
INSERT INTO key_pairs (bot_id, private_key_jwk, public_key_jwk)
|
|
318
|
+
VALUES (?, ?, ?)
|
|
99
319
|
`);
|
|
100
320
|
this.db.exec("BEGIN TRANSACTION");
|
|
101
321
|
try {
|
|
102
|
-
deleteStmt.run();
|
|
322
|
+
deleteStmt.run(identifier);
|
|
103
323
|
for (const keyPair of keyPairs) {
|
|
104
324
|
const privateJwk = await exportJwk(keyPair.privateKey);
|
|
105
325
|
const publicJwk = await exportJwk(keyPair.publicKey);
|
|
106
|
-
insertStmt.run(JSON.stringify(privateJwk), JSON.stringify(publicJwk));
|
|
326
|
+
insertStmt.run(identifier, JSON.stringify(privateJwk), JSON.stringify(publicJwk));
|
|
107
327
|
}
|
|
108
328
|
this.db.exec("COMMIT");
|
|
109
329
|
} catch (error) {
|
|
@@ -111,11 +331,12 @@ var SqliteRepository = class {
|
|
|
111
331
|
throw error;
|
|
112
332
|
}
|
|
113
333
|
}
|
|
114
|
-
async getKeyPairs() {
|
|
334
|
+
async getKeyPairs(identifier) {
|
|
115
335
|
const stmt = this.db.prepare(`
|
|
116
336
|
SELECT private_key_jwk, public_key_jwk FROM key_pairs
|
|
337
|
+
WHERE bot_id = ? ORDER BY id
|
|
117
338
|
`);
|
|
118
|
-
const rows = stmt.all();
|
|
339
|
+
const rows = stmt.all(identifier);
|
|
119
340
|
if (rows.length === 0) return void 0;
|
|
120
341
|
const keyPairs = [];
|
|
121
342
|
for (const row of rows) {
|
|
@@ -128,20 +349,20 @@ var SqliteRepository = class {
|
|
|
128
349
|
}
|
|
129
350
|
return keyPairs;
|
|
130
351
|
}
|
|
131
|
-
async addMessage(id, activity) {
|
|
352
|
+
async addMessage(identifier, id, activity) {
|
|
132
353
|
const stmt = this.db.prepare(`
|
|
133
|
-
INSERT INTO messages (id, activity_json, published)
|
|
134
|
-
VALUES (?, ?, ?)
|
|
354
|
+
INSERT INTO messages (bot_id, id, activity_json, published)
|
|
355
|
+
VALUES (?, ?, ?, ?)
|
|
135
356
|
`);
|
|
136
357
|
const activityJson = JSON.stringify(await activity.toJsonLd({ format: "compact" }));
|
|
137
358
|
const published = activity.published?.epochMilliseconds ?? null;
|
|
138
|
-
stmt.run(id, activityJson, published);
|
|
359
|
+
stmt.run(identifier, id, activityJson, published);
|
|
139
360
|
}
|
|
140
|
-
async updateMessage(id, updater) {
|
|
361
|
+
async updateMessage(identifier, id, updater) {
|
|
141
362
|
const selectStmt = this.db.prepare(`
|
|
142
|
-
SELECT activity_json FROM messages WHERE id = ?
|
|
363
|
+
SELECT activity_json FROM messages WHERE bot_id = ? AND id = ?
|
|
143
364
|
`);
|
|
144
|
-
const row = selectStmt.get(id);
|
|
365
|
+
const row = selectStmt.get(identifier, id);
|
|
145
366
|
if (!row) return false;
|
|
146
367
|
const activityData = JSON.parse(row.activity_json);
|
|
147
368
|
const activity = await Activity.fromJsonLd(activityData);
|
|
@@ -149,25 +370,25 @@ var SqliteRepository = class {
|
|
|
149
370
|
const newActivity = await updater(activity);
|
|
150
371
|
if (newActivity == null) return false;
|
|
151
372
|
const updateStmt = this.db.prepare(`
|
|
152
|
-
UPDATE messages
|
|
153
|
-
SET activity_json = ?, published = ?
|
|
154
|
-
WHERE id = ?
|
|
373
|
+
UPDATE messages
|
|
374
|
+
SET activity_json = ?, published = ?
|
|
375
|
+
WHERE bot_id = ? AND id = ?
|
|
155
376
|
`);
|
|
156
377
|
const newActivityJson = JSON.stringify(await newActivity.toJsonLd({ format: "compact" }));
|
|
157
378
|
const published = newActivity.published?.epochMilliseconds ?? null;
|
|
158
|
-
updateStmt.run(newActivityJson, published, id);
|
|
379
|
+
updateStmt.run(newActivityJson, published, identifier, id);
|
|
159
380
|
return true;
|
|
160
381
|
}
|
|
161
|
-
async removeMessage(id) {
|
|
382
|
+
async removeMessage(identifier, id) {
|
|
162
383
|
const selectStmt = this.db.prepare(`
|
|
163
|
-
SELECT activity_json FROM messages WHERE id = ?
|
|
384
|
+
SELECT activity_json FROM messages WHERE bot_id = ? AND id = ?
|
|
164
385
|
`);
|
|
165
|
-
const row = selectStmt.get(id);
|
|
386
|
+
const row = selectStmt.get(identifier, id);
|
|
166
387
|
if (!row) return void 0;
|
|
167
388
|
const deleteStmt = this.db.prepare(`
|
|
168
|
-
DELETE FROM messages WHERE id = ?
|
|
389
|
+
DELETE FROM messages WHERE bot_id = ? AND id = ?
|
|
169
390
|
`);
|
|
170
|
-
deleteStmt.run(id);
|
|
391
|
+
deleteStmt.run(identifier, id);
|
|
171
392
|
try {
|
|
172
393
|
const activityData = JSON.parse(row.activity_json);
|
|
173
394
|
const activity = await Activity.fromJsonLd(activityData);
|
|
@@ -180,10 +401,10 @@ var SqliteRepository = class {
|
|
|
180
401
|
}
|
|
181
402
|
return void 0;
|
|
182
403
|
}
|
|
183
|
-
async *getMessages(options = {}) {
|
|
404
|
+
async *getMessages(identifier, options = {}) {
|
|
184
405
|
const { order = "newest", until, since, limit } = options;
|
|
185
|
-
let sql = "SELECT activity_json FROM messages WHERE
|
|
186
|
-
const params = [];
|
|
406
|
+
let sql = "SELECT activity_json FROM messages WHERE bot_id = ?";
|
|
407
|
+
const params = [identifier];
|
|
187
408
|
if (since != null) {
|
|
188
409
|
sql += " AND published >= ?";
|
|
189
410
|
params.push(since.epochMilliseconds);
|
|
@@ -208,11 +429,11 @@ var SqliteRepository = class {
|
|
|
208
429
|
continue;
|
|
209
430
|
}
|
|
210
431
|
}
|
|
211
|
-
async getMessage(id) {
|
|
432
|
+
async getMessage(identifier, id) {
|
|
212
433
|
const stmt = this.db.prepare(`
|
|
213
|
-
SELECT activity_json FROM messages WHERE id = ?
|
|
434
|
+
SELECT activity_json FROM messages WHERE bot_id = ? AND id = ?
|
|
214
435
|
`);
|
|
215
|
-
const row = stmt.get(id);
|
|
436
|
+
const row = stmt.get(identifier, id);
|
|
216
437
|
if (!row) return void 0;
|
|
217
438
|
try {
|
|
218
439
|
const activityData = JSON.parse(row.activity_json);
|
|
@@ -226,51 +447,53 @@ var SqliteRepository = class {
|
|
|
226
447
|
}
|
|
227
448
|
return void 0;
|
|
228
449
|
}
|
|
229
|
-
countMessages() {
|
|
230
|
-
const stmt = this.db.prepare("SELECT COUNT(*) as count FROM messages");
|
|
231
|
-
const row = stmt.get();
|
|
450
|
+
countMessages(identifier) {
|
|
451
|
+
const stmt = this.db.prepare("SELECT COUNT(*) as count FROM messages WHERE bot_id = ?");
|
|
452
|
+
const row = stmt.get(identifier);
|
|
232
453
|
return Promise.resolve(row.count);
|
|
233
454
|
}
|
|
234
|
-
async addFollower(followRequestId, follower) {
|
|
455
|
+
async addFollower(identifier, followRequestId, follower) {
|
|
235
456
|
if (follower.id == null) throw new TypeError("The follower ID is missing.");
|
|
236
457
|
const followerJson = JSON.stringify(await follower.toJsonLd({ format: "compact" }));
|
|
237
458
|
const insertFollowerStmt = this.db.prepare(`
|
|
238
|
-
INSERT OR REPLACE INTO followers (follower_id, actor_json)
|
|
239
|
-
VALUES (?, ?)
|
|
459
|
+
INSERT OR REPLACE INTO followers (bot_id, follower_id, actor_json)
|
|
460
|
+
VALUES (?, ?, ?)
|
|
240
461
|
`);
|
|
241
462
|
const insertRequestStmt = this.db.prepare(`
|
|
242
|
-
INSERT OR REPLACE INTO follow_requests
|
|
243
|
-
|
|
463
|
+
INSERT OR REPLACE INTO follow_requests
|
|
464
|
+
(bot_id, follow_request_id, follower_id)
|
|
465
|
+
VALUES (?, ?, ?)
|
|
244
466
|
`);
|
|
245
467
|
this.db.exec("BEGIN TRANSACTION");
|
|
246
468
|
try {
|
|
247
|
-
insertFollowerStmt.run(follower.id.href, followerJson);
|
|
248
|
-
insertRequestStmt.run(followRequestId.href, follower.id.href);
|
|
469
|
+
insertFollowerStmt.run(identifier, follower.id.href, followerJson);
|
|
470
|
+
insertRequestStmt.run(identifier, followRequestId.href, follower.id.href);
|
|
249
471
|
this.db.exec("COMMIT");
|
|
250
472
|
} catch (error) {
|
|
251
473
|
this.db.exec("ROLLBACK");
|
|
252
474
|
throw error;
|
|
253
475
|
}
|
|
254
476
|
}
|
|
255
|
-
async removeFollower(followRequestId, actorId) {
|
|
477
|
+
async removeFollower(identifier, followRequestId, actorId) {
|
|
256
478
|
const checkStmt = this.db.prepare(`
|
|
257
|
-
SELECT fr.follower_id, f.actor_json
|
|
258
|
-
FROM follow_requests fr
|
|
259
|
-
JOIN followers f
|
|
260
|
-
|
|
479
|
+
SELECT fr.follower_id, f.actor_json
|
|
480
|
+
FROM follow_requests fr
|
|
481
|
+
JOIN followers f
|
|
482
|
+
ON fr.bot_id = f.bot_id AND fr.follower_id = f.follower_id
|
|
483
|
+
WHERE fr.bot_id = ? AND fr.follow_request_id = ? AND fr.follower_id = ?
|
|
261
484
|
`);
|
|
262
|
-
const row = checkStmt.get(followRequestId.href, actorId.href);
|
|
485
|
+
const row = checkStmt.get(identifier, followRequestId.href, actorId.href);
|
|
263
486
|
if (!row) return void 0;
|
|
264
487
|
const deleteRequestStmt = this.db.prepare(`
|
|
265
|
-
DELETE FROM follow_requests WHERE follow_request_id = ?
|
|
488
|
+
DELETE FROM follow_requests WHERE bot_id = ? AND follow_request_id = ?
|
|
266
489
|
`);
|
|
267
490
|
const deleteFollowerStmt = this.db.prepare(`
|
|
268
|
-
DELETE FROM followers WHERE follower_id = ?
|
|
491
|
+
DELETE FROM followers WHERE bot_id = ? AND follower_id = ?
|
|
269
492
|
`);
|
|
270
493
|
this.db.exec("BEGIN TRANSACTION");
|
|
271
494
|
try {
|
|
272
|
-
deleteRequestStmt.run(followRequestId.href);
|
|
273
|
-
deleteFollowerStmt.run(actorId.href);
|
|
495
|
+
deleteRequestStmt.run(identifier, followRequestId.href);
|
|
496
|
+
deleteFollowerStmt.run(identifier, actorId.href);
|
|
274
497
|
this.db.exec("COMMIT");
|
|
275
498
|
} catch (error) {
|
|
276
499
|
this.db.exec("ROLLBACK");
|
|
@@ -285,17 +508,17 @@ var SqliteRepository = class {
|
|
|
285
508
|
}
|
|
286
509
|
return void 0;
|
|
287
510
|
}
|
|
288
|
-
hasFollower(followerId) {
|
|
511
|
+
hasFollower(identifier, followerId) {
|
|
289
512
|
const stmt = this.db.prepare(`
|
|
290
|
-
SELECT 1 FROM followers WHERE follower_id = ?
|
|
513
|
+
SELECT 1 FROM followers WHERE bot_id = ? AND follower_id = ?
|
|
291
514
|
`);
|
|
292
|
-
const row = stmt.get(followerId.href);
|
|
515
|
+
const row = stmt.get(identifier, followerId.href);
|
|
293
516
|
return Promise.resolve(row != null);
|
|
294
517
|
}
|
|
295
|
-
async *getFollowers(options = {}) {
|
|
518
|
+
async *getFollowers(identifier, options = {}) {
|
|
296
519
|
const { offset = 0, limit } = options;
|
|
297
|
-
let sql = "SELECT actor_json FROM followers ORDER BY follower_id";
|
|
298
|
-
const params = [];
|
|
520
|
+
let sql = "SELECT actor_json FROM followers WHERE bot_id = ? ORDER BY follower_id";
|
|
521
|
+
const params = [identifier];
|
|
299
522
|
if (limit != null) {
|
|
300
523
|
sql += " LIMIT ? OFFSET ?";
|
|
301
524
|
params.push(limit, offset);
|
|
@@ -314,31 +537,31 @@ var SqliteRepository = class {
|
|
|
314
537
|
continue;
|
|
315
538
|
}
|
|
316
539
|
}
|
|
317
|
-
countFollowers() {
|
|
318
|
-
const stmt = this.db.prepare("SELECT COUNT(*) as count FROM followers");
|
|
319
|
-
const row = stmt.get();
|
|
540
|
+
countFollowers(identifier) {
|
|
541
|
+
const stmt = this.db.prepare("SELECT COUNT(*) as count FROM followers WHERE bot_id = ?");
|
|
542
|
+
const row = stmt.get(identifier);
|
|
320
543
|
return Promise.resolve(row.count);
|
|
321
544
|
}
|
|
322
|
-
async addSentFollow(id, follow) {
|
|
545
|
+
async addSentFollow(identifier, id, follow) {
|
|
323
546
|
const stmt = this.db.prepare(`
|
|
324
|
-
INSERT OR REPLACE INTO sent_follows (id, follow_json)
|
|
325
|
-
VALUES (?, ?)
|
|
547
|
+
INSERT OR REPLACE INTO sent_follows (bot_id, id, follow_json)
|
|
548
|
+
VALUES (?, ?, ?)
|
|
326
549
|
`);
|
|
327
550
|
const followJson = JSON.stringify(await follow.toJsonLd({ format: "compact" }));
|
|
328
|
-
stmt.run(id, followJson);
|
|
551
|
+
stmt.run(identifier, id, followJson);
|
|
329
552
|
}
|
|
330
|
-
async removeSentFollow(id) {
|
|
331
|
-
const follow = await this.getSentFollow(id);
|
|
553
|
+
async removeSentFollow(identifier, id) {
|
|
554
|
+
const follow = await this.getSentFollow(identifier, id);
|
|
332
555
|
if (follow == null) return void 0;
|
|
333
|
-
const stmt = this.db.prepare("DELETE FROM sent_follows WHERE id = ?");
|
|
334
|
-
stmt.run(id);
|
|
556
|
+
const stmt = this.db.prepare("DELETE FROM sent_follows WHERE bot_id = ? AND id = ?");
|
|
557
|
+
stmt.run(identifier, id);
|
|
335
558
|
return follow;
|
|
336
559
|
}
|
|
337
|
-
async getSentFollow(id) {
|
|
560
|
+
async getSentFollow(identifier, id) {
|
|
338
561
|
const stmt = this.db.prepare(`
|
|
339
|
-
SELECT follow_json FROM sent_follows WHERE id = ?
|
|
562
|
+
SELECT follow_json FROM sent_follows WHERE bot_id = ? AND id = ?
|
|
340
563
|
`);
|
|
341
|
-
const row = stmt.get(id);
|
|
564
|
+
const row = stmt.get(identifier, id);
|
|
342
565
|
if (!row) return void 0;
|
|
343
566
|
try {
|
|
344
567
|
const followData = JSON.parse(row.follow_json);
|
|
@@ -351,26 +574,26 @@ var SqliteRepository = class {
|
|
|
351
574
|
return void 0;
|
|
352
575
|
}
|
|
353
576
|
}
|
|
354
|
-
async addFollowee(followeeId, follow) {
|
|
577
|
+
async addFollowee(identifier, followeeId, follow) {
|
|
355
578
|
const stmt = this.db.prepare(`
|
|
356
|
-
INSERT OR REPLACE INTO followees (followee_id, follow_json)
|
|
357
|
-
VALUES (?, ?)
|
|
579
|
+
INSERT OR REPLACE INTO followees (bot_id, followee_id, follow_json)
|
|
580
|
+
VALUES (?, ?, ?)
|
|
358
581
|
`);
|
|
359
582
|
const followJson = JSON.stringify(await follow.toJsonLd({ format: "compact" }));
|
|
360
|
-
stmt.run(followeeId.href, followJson);
|
|
583
|
+
stmt.run(identifier, followeeId.href, followJson);
|
|
361
584
|
}
|
|
362
|
-
async removeFollowee(followeeId) {
|
|
363
|
-
const follow = await this.getFollowee(followeeId);
|
|
585
|
+
async removeFollowee(identifier, followeeId) {
|
|
586
|
+
const follow = await this.getFollowee(identifier, followeeId);
|
|
364
587
|
if (follow == null) return void 0;
|
|
365
|
-
const stmt = this.db.prepare("DELETE FROM followees WHERE followee_id = ?");
|
|
366
|
-
stmt.run(followeeId.href);
|
|
588
|
+
const stmt = this.db.prepare("DELETE FROM followees WHERE bot_id = ? AND followee_id = ?");
|
|
589
|
+
stmt.run(identifier, followeeId.href);
|
|
367
590
|
return follow;
|
|
368
591
|
}
|
|
369
|
-
async getFollowee(followeeId) {
|
|
592
|
+
async getFollowee(identifier, followeeId) {
|
|
370
593
|
const stmt = this.db.prepare(`
|
|
371
|
-
SELECT follow_json FROM followees WHERE followee_id = ?
|
|
594
|
+
SELECT follow_json FROM followees WHERE bot_id = ? AND followee_id = ?
|
|
372
595
|
`);
|
|
373
|
-
const row = stmt.get(followeeId.href);
|
|
596
|
+
const row = stmt.get(identifier, followeeId.href);
|
|
374
597
|
if (!row) return void 0;
|
|
375
598
|
try {
|
|
376
599
|
const followData = JSON.parse(row.follow_json);
|
|
@@ -383,35 +606,45 @@ var SqliteRepository = class {
|
|
|
383
606
|
return void 0;
|
|
384
607
|
}
|
|
385
608
|
}
|
|
386
|
-
|
|
609
|
+
async *findFollowedBots(followeeId) {
|
|
387
610
|
const stmt = this.db.prepare(`
|
|
388
|
-
|
|
389
|
-
|
|
611
|
+
SELECT bot_id FROM followees WHERE followee_id = ? ORDER BY bot_id
|
|
612
|
+
`);
|
|
613
|
+
const rows = stmt.all(followeeId.href);
|
|
614
|
+
for (const row of rows) yield row.bot_id;
|
|
615
|
+
}
|
|
616
|
+
vote(identifier, messageId, voterId, option) {
|
|
617
|
+
const stmt = this.db.prepare(`
|
|
618
|
+
INSERT OR IGNORE INTO poll_votes (bot_id, message_id, voter_id, option)
|
|
619
|
+
VALUES (?, ?, ?, ?)
|
|
390
620
|
`);
|
|
391
|
-
stmt.run(messageId, voterId.href, option);
|
|
621
|
+
stmt.run(identifier, messageId, voterId.href, option);
|
|
392
622
|
return Promise.resolve();
|
|
393
623
|
}
|
|
394
|
-
countVoters(messageId) {
|
|
624
|
+
countVoters(identifier, messageId) {
|
|
395
625
|
const stmt = this.db.prepare(`
|
|
396
|
-
SELECT COUNT(DISTINCT voter_id) as count
|
|
397
|
-
FROM poll_votes
|
|
398
|
-
WHERE message_id = ?
|
|
626
|
+
SELECT COUNT(DISTINCT voter_id) as count
|
|
627
|
+
FROM poll_votes
|
|
628
|
+
WHERE bot_id = ? AND message_id = ?
|
|
399
629
|
`);
|
|
400
|
-
const row = stmt.get(messageId);
|
|
630
|
+
const row = stmt.get(identifier, messageId);
|
|
401
631
|
return Promise.resolve(row.count);
|
|
402
632
|
}
|
|
403
|
-
countVotes(messageId) {
|
|
633
|
+
countVotes(identifier, messageId) {
|
|
404
634
|
const stmt = this.db.prepare(`
|
|
405
|
-
SELECT option, COUNT(*) as count
|
|
406
|
-
FROM poll_votes
|
|
407
|
-
WHERE message_id = ?
|
|
635
|
+
SELECT option, COUNT(*) as count
|
|
636
|
+
FROM poll_votes
|
|
637
|
+
WHERE bot_id = ? AND message_id = ?
|
|
408
638
|
GROUP BY option
|
|
409
639
|
`);
|
|
410
|
-
const rows = stmt.all(messageId);
|
|
640
|
+
const rows = stmt.all(identifier, messageId);
|
|
411
641
|
const result = {};
|
|
412
642
|
for (const row of rows) result[row.option] = row.count;
|
|
413
643
|
return Promise.resolve(result);
|
|
414
644
|
}
|
|
645
|
+
forIdentifier(identifier) {
|
|
646
|
+
return new ActorScopedRepository(this, identifier);
|
|
647
|
+
}
|
|
415
648
|
};
|
|
416
649
|
|
|
417
650
|
//#endregion
|
package/dist/mod.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.js","names":["options: SqliteRepositoryOptions","keyPairs: CryptoKeyPair[]","id: Uuid","activity: Create | Announce","updater: (\n existing: Create | Announce,\n ) => Create | Announce | undefined | Promise<Create | Announce | undefined>","options: RepositoryGetMessagesOptions","params: (number | string)[]","followRequestId: URL","follower: Actor","actorId: URL","followerId: URL","options: RepositoryGetFollowersOptions","params: number[]","follow: Follow","followeeId: URL","messageId: Uuid","voterId: URL","option: string","result: Record<string, number>"],"sources":["../src/mod.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 {\n Repository,\n RepositoryGetFollowersOptions,\n RepositoryGetMessagesOptions,\n Uuid,\n} from \"@fedify/botkit/repository\";\nimport { exportJwk, importJwk } from \"@fedify/fedify/sig\";\nimport {\n Activity,\n type Actor,\n Announce,\n Create,\n Follow,\n isActor,\n Object,\n} from \"@fedify/vocab\";\nimport { getLogger } from \"@logtape/logtape\";\nimport { DatabaseSync } from \"node:sqlite\";\n\nconst logger = getLogger([\"botkit\", \"sqlite\"]);\n\n/**\n * Options for creating a SQLite repository.\n * @since 0.3.0\n */\nexport interface SqliteRepositoryOptions {\n /**\n * The path to the SQLite database file.\n * If not provided, an in-memory database will be used.\n */\n readonly path?: string;\n\n /**\n * Whether to enable Write-Ahead Logging (WAL) mode.\n * @default true\n */\n readonly wal?: boolean;\n}\n\n/**\n * A repository for storing bot data using SQLite.\n * @since 0.3.0\n */\nexport class SqliteRepository implements Repository, Disposable {\n private readonly db: DatabaseSync;\n\n /**\n * Creates a new SQLite repository.\n * @param options The options for creating the repository.\n */\n constructor(options: SqliteRepositoryOptions = {}) {\n const { path = \":memory:\", wal = true } = options;\n\n this.db = new DatabaseSync(path);\n\n // Enable foreign key constraints\n this.db.exec(\"PRAGMA foreign_keys = ON;\");\n\n if (wal && path !== \":memory:\") {\n this.db.exec(\"PRAGMA journal_mode = WAL;\");\n }\n\n this.initializeTables();\n }\n\n [Symbol.dispose]() {\n this.close();\n }\n\n /**\n * Closes the database connection.\n */\n close(): void {\n this.db.close();\n }\n\n private initializeTables(): void {\n // Key pairs table\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS key_pairs (\n id INTEGER PRIMARY KEY,\n private_key_jwk TEXT NOT NULL,\n public_key_jwk TEXT NOT NULL\n )\n `);\n\n // Messages table\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS messages (\n id TEXT PRIMARY KEY,\n activity_json TEXT NOT NULL,\n published INTEGER\n )\n `);\n\n // Create index on published timestamp for efficient ordering\n this.db.exec(`\n CREATE INDEX IF NOT EXISTS idx_messages_published ON messages(published)\n `);\n\n // Followers table\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS followers (\n follower_id TEXT PRIMARY KEY,\n actor_json TEXT NOT NULL\n )\n `);\n\n // Follow requests mapping table\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS follow_requests (\n follow_request_id TEXT PRIMARY KEY,\n follower_id TEXT NOT NULL,\n FOREIGN KEY (follower_id) REFERENCES followers(follower_id)\n )\n `);\n\n // Sent follows table\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS sent_follows (\n id TEXT PRIMARY KEY,\n follow_json TEXT NOT NULL\n )\n `);\n\n // Followees table\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS followees (\n followee_id TEXT PRIMARY KEY,\n follow_json TEXT NOT NULL\n )\n `);\n\n // Poll votes table\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS poll_votes (\n message_id TEXT NOT NULL,\n voter_id TEXT NOT NULL,\n option TEXT NOT NULL,\n PRIMARY KEY (message_id, voter_id, option)\n )\n `);\n\n // Create index for efficient vote counting\n this.db.exec(`\n CREATE INDEX IF NOT EXISTS idx_poll_votes_message_option \n ON poll_votes(message_id, option)\n `);\n }\n\n async setKeyPairs(keyPairs: CryptoKeyPair[]): Promise<void> {\n const deleteStmt = this.db.prepare(\"DELETE FROM key_pairs\");\n const insertStmt = this.db.prepare(`\n INSERT INTO key_pairs (private_key_jwk, public_key_jwk) \n VALUES (?, ?)\n `);\n\n this.db.exec(\"BEGIN TRANSACTION\");\n try {\n deleteStmt.run();\n\n for (const keyPair of keyPairs) {\n const privateJwk = await exportJwk(keyPair.privateKey);\n const publicJwk = await exportJwk(keyPair.publicKey);\n insertStmt.run(JSON.stringify(privateJwk), JSON.stringify(publicJwk));\n }\n\n this.db.exec(\"COMMIT\");\n } catch (error) {\n this.db.exec(\"ROLLBACK\");\n throw error;\n }\n }\n\n async getKeyPairs(): Promise<CryptoKeyPair[] | undefined> {\n const stmt = this.db.prepare(`\n SELECT private_key_jwk, public_key_jwk FROM key_pairs\n `);\n const rows = stmt.all() as Array<{\n private_key_jwk: string;\n public_key_jwk: string;\n }>;\n\n if (rows.length === 0) return undefined;\n\n const keyPairs: CryptoKeyPair[] = [];\n for (const row of rows) {\n const privateJwk = JSON.parse(row.private_key_jwk);\n const publicJwk = JSON.parse(row.public_key_jwk);\n\n keyPairs.push({\n privateKey: await importJwk(privateJwk, \"private\"),\n publicKey: await importJwk(publicJwk, \"public\"),\n });\n }\n\n return keyPairs;\n }\n\n async addMessage(id: Uuid, activity: Create | Announce): Promise<void> {\n const stmt = this.db.prepare(`\n INSERT INTO messages (id, activity_json, published) \n VALUES (?, ?, ?)\n `);\n\n const activityJson = JSON.stringify(\n await activity.toJsonLd({ format: \"compact\" }),\n );\n const published = activity.published?.epochMilliseconds ?? null;\n\n stmt.run(id, activityJson, published);\n }\n\n async updateMessage(\n id: Uuid,\n updater: (\n existing: Create | Announce,\n ) => Create | Announce | undefined | Promise<Create | Announce | undefined>,\n ): Promise<boolean> {\n const selectStmt = this.db.prepare(`\n SELECT activity_json FROM messages WHERE id = ?\n `);\n const row = selectStmt.get(id) as { activity_json: string } | undefined;\n\n if (!row) return false;\n\n const activityData = JSON.parse(row.activity_json);\n const activity = await Activity.fromJsonLd(activityData);\n\n if (!(activity instanceof Create || activity instanceof Announce)) {\n return false;\n }\n\n const newActivity = await updater(activity);\n if (newActivity == null) return false;\n\n const updateStmt = this.db.prepare(`\n UPDATE messages \n SET activity_json = ?, published = ? \n WHERE id = ?\n `);\n\n const newActivityJson = JSON.stringify(\n await newActivity.toJsonLd({ format: \"compact\" }),\n );\n const published = newActivity.published?.epochMilliseconds ?? null;\n\n updateStmt.run(newActivityJson, published, id);\n return true;\n }\n\n async removeMessage(id: Uuid): Promise<Create | Announce | undefined> {\n const selectStmt = this.db.prepare(`\n SELECT activity_json FROM messages WHERE id = ?\n `);\n const row = selectStmt.get(id) as { activity_json: string } | undefined;\n\n if (!row) return undefined;\n\n const deleteStmt = this.db.prepare(`\n DELETE FROM messages WHERE id = ?\n `);\n deleteStmt.run(id);\n\n try {\n const activityData = JSON.parse(row.activity_json);\n const activity = await Activity.fromJsonLd(activityData);\n\n if (activity instanceof Create || activity instanceof Announce) {\n return activity;\n }\n } catch (error) {\n logger.warn(\"Failed to parse removed message activity\", { id, error });\n }\n\n return undefined;\n }\n\n async *getMessages(\n options: RepositoryGetMessagesOptions = {},\n ): AsyncIterable<Create | Announce> {\n const { order = \"newest\", until, since, limit } = options;\n\n let sql = \"SELECT activity_json FROM messages WHERE 1=1\";\n const params: (number | string)[] = [];\n\n if (since != null) {\n sql += \" AND published >= ?\";\n params.push(since.epochMilliseconds);\n }\n\n if (until != null) {\n sql += \" AND published <= ?\";\n params.push(until.epochMilliseconds);\n }\n\n sql += order === \"oldest\"\n ? \" ORDER BY published ASC\"\n : \" ORDER BY published DESC\";\n\n if (limit != null) {\n sql += \" LIMIT ?\";\n params.push(limit);\n }\n\n const stmt = this.db.prepare(sql);\n const rows = stmt.all(...params) as Array<{ activity_json: string }>;\n\n for (const row of rows) {\n try {\n const activityData = JSON.parse(row.activity_json);\n const activity = await Activity.fromJsonLd(activityData);\n\n if (activity instanceof Create || activity instanceof Announce) {\n yield activity;\n }\n } catch (error) {\n logger.warn(\"Failed to parse message activity\", { error });\n continue;\n }\n }\n }\n\n async getMessage(id: Uuid): Promise<Create | Announce | undefined> {\n const stmt = this.db.prepare(`\n SELECT activity_json FROM messages WHERE id = ?\n `);\n const row = stmt.get(id) as { activity_json: string } | undefined;\n\n if (!row) return undefined;\n\n try {\n const activityData = JSON.parse(row.activity_json);\n const activity = await Activity.fromJsonLd(activityData);\n\n if (activity instanceof Create || activity instanceof Announce) {\n return activity;\n }\n } catch (error) {\n logger.warn(\"Failed to parse message activity\", { id, error });\n }\n\n return undefined;\n }\n\n countMessages(): Promise<number> {\n const stmt = this.db.prepare(\"SELECT COUNT(*) as count FROM messages\");\n const row = stmt.get() as { count: number };\n return Promise.resolve(row.count);\n }\n\n async addFollower(followRequestId: URL, follower: Actor): Promise<void> {\n if (follower.id == null) {\n throw new TypeError(\"The follower ID is missing.\");\n }\n\n const followerJson = JSON.stringify(\n await follower.toJsonLd({ format: \"compact\" }),\n );\n\n const insertFollowerStmt = this.db.prepare(`\n INSERT OR REPLACE INTO followers (follower_id, actor_json) \n VALUES (?, ?)\n `);\n\n const insertRequestStmt = this.db.prepare(`\n INSERT OR REPLACE INTO follow_requests (follow_request_id, follower_id) \n VALUES (?, ?)\n `);\n\n this.db.exec(\"BEGIN TRANSACTION\");\n try {\n insertFollowerStmt.run(follower.id.href, followerJson);\n insertRequestStmt.run(followRequestId.href, follower.id.href);\n this.db.exec(\"COMMIT\");\n } catch (error) {\n this.db.exec(\"ROLLBACK\");\n throw error;\n }\n }\n\n async removeFollower(\n followRequestId: URL,\n actorId: URL,\n ): Promise<Actor | undefined> {\n // Check if the follow request exists and matches the actor\n const checkStmt = this.db.prepare(`\n SELECT fr.follower_id, f.actor_json \n FROM follow_requests fr \n JOIN followers f ON fr.follower_id = f.follower_id \n WHERE fr.follow_request_id = ? AND fr.follower_id = ?\n `);\n\n const row = checkStmt.get(followRequestId.href, actorId.href) as {\n follower_id: string;\n actor_json: string;\n } | undefined;\n\n if (!row) return undefined;\n\n // Remove the follower and follow request\n const deleteRequestStmt = this.db.prepare(`\n DELETE FROM follow_requests WHERE follow_request_id = ?\n `);\n\n const deleteFollowerStmt = this.db.prepare(`\n DELETE FROM followers WHERE follower_id = ?\n `);\n\n this.db.exec(\"BEGIN TRANSACTION\");\n try {\n deleteRequestStmt.run(followRequestId.href);\n deleteFollowerStmt.run(actorId.href);\n this.db.exec(\"COMMIT\");\n } catch (error) {\n this.db.exec(\"ROLLBACK\");\n throw error;\n }\n\n try {\n const actorData = JSON.parse(row.actor_json);\n const actor = await Object.fromJsonLd(actorData);\n\n if (isActor(actor)) {\n return actor;\n }\n } catch (error) {\n logger.warn(\"Failed to parse removed follower actor\", { error });\n }\n\n return undefined;\n }\n\n hasFollower(followerId: URL): Promise<boolean> {\n const stmt = this.db.prepare(`\n SELECT 1 FROM followers WHERE follower_id = ?\n `);\n const row = stmt.get(followerId.href);\n return Promise.resolve(row != null);\n }\n\n async *getFollowers(\n options: RepositoryGetFollowersOptions = {},\n ): AsyncIterable<Actor> {\n const { offset = 0, limit } = options;\n\n let sql = \"SELECT actor_json FROM followers ORDER BY follower_id\";\n const params: number[] = [];\n\n if (limit != null) {\n sql += \" LIMIT ? OFFSET ?\";\n params.push(limit, offset);\n } else if (offset > 0) {\n sql += \" LIMIT -1 OFFSET ?\";\n params.push(offset);\n }\n\n const stmt = this.db.prepare(sql);\n const rows = stmt.all(...params) as { actor_json: string }[];\n\n for (const row of rows) {\n try {\n const actorData = JSON.parse(row.actor_json);\n const actor = await Object.fromJsonLd(actorData);\n\n if (isActor(actor)) {\n yield actor;\n }\n } catch (error) {\n logger.warn(\"Failed to parse follower actor\", { error });\n continue;\n }\n }\n }\n\n countFollowers(): Promise<number> {\n const stmt = this.db.prepare(\"SELECT COUNT(*) as count FROM followers\");\n const row = stmt.get() as { count: number };\n return Promise.resolve(row.count);\n }\n\n async addSentFollow(id: Uuid, follow: Follow): Promise<void> {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO sent_follows (id, follow_json) \n VALUES (?, ?)\n `);\n\n const followJson = JSON.stringify(\n await follow.toJsonLd({ format: \"compact\" }),\n );\n\n stmt.run(id, followJson);\n }\n\n async removeSentFollow(id: Uuid): Promise<Follow | undefined> {\n const follow = await this.getSentFollow(id);\n if (follow == null) return undefined;\n\n const stmt = this.db.prepare(\"DELETE FROM sent_follows WHERE id = ?\");\n stmt.run(id);\n\n return follow;\n }\n\n async getSentFollow(id: Uuid): Promise<Follow | undefined> {\n const stmt = this.db.prepare(`\n SELECT follow_json FROM sent_follows WHERE id = ?\n `);\n const row = stmt.get(id) as { follow_json: string } | undefined;\n\n if (!row) return undefined;\n\n try {\n const followData = JSON.parse(row.follow_json);\n return await Follow.fromJsonLd(followData);\n } catch (error) {\n logger.warn(\"Failed to parse sent follow activity\", { id, error });\n return undefined;\n }\n }\n\n async addFollowee(followeeId: URL, follow: Follow): Promise<void> {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO followees (followee_id, follow_json) \n VALUES (?, ?)\n `);\n\n const followJson = JSON.stringify(\n await follow.toJsonLd({ format: \"compact\" }),\n );\n\n stmt.run(followeeId.href, followJson);\n }\n\n async removeFollowee(followeeId: URL): Promise<Follow | undefined> {\n const follow = await this.getFollowee(followeeId);\n if (follow == null) return undefined;\n\n const stmt = this.db.prepare(\"DELETE FROM followees WHERE followee_id = ?\");\n stmt.run(followeeId.href);\n\n return follow;\n }\n\n async getFollowee(followeeId: URL): Promise<Follow | undefined> {\n const stmt = this.db.prepare(`\n SELECT follow_json FROM followees WHERE followee_id = ?\n `);\n const row = stmt.get(followeeId.href) as\n | { follow_json: string }\n | undefined;\n\n if (!row) return undefined;\n\n try {\n const followData = JSON.parse(row.follow_json);\n return await Follow.fromJsonLd(followData);\n } catch (error) {\n logger.warn(\"Failed to parse followee activity\", {\n followeeId: followeeId.href,\n error,\n });\n return undefined;\n }\n }\n\n vote(messageId: Uuid, voterId: URL, option: string): Promise<void> {\n const stmt = this.db.prepare(`\n INSERT OR IGNORE INTO poll_votes (message_id, voter_id, option) \n VALUES (?, ?, ?)\n `);\n\n stmt.run(messageId, voterId.href, option);\n return Promise.resolve();\n }\n\n countVoters(messageId: Uuid): Promise<number> {\n const stmt = this.db.prepare(`\n SELECT COUNT(DISTINCT voter_id) as count \n FROM poll_votes \n WHERE message_id = ?\n `);\n const row = stmt.get(messageId) as { count: number };\n return Promise.resolve(row.count);\n }\n\n countVotes(messageId: Uuid): Promise<Readonly<Record<string, number>>> {\n const stmt = this.db.prepare(`\n SELECT option, COUNT(*) as count \n FROM poll_votes \n WHERE message_id = ? \n GROUP BY option\n `);\n const rows = stmt.all(messageId) as Array<{\n option: string;\n count: number;\n }>;\n\n const result: Record<string, number> = {};\n for (const row of rows) {\n result[row.option] = row.count;\n }\n\n return Promise.resolve(result);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mod.js","names":["options: SqliteRepositoryOptions","table: string","identifier: string","keyPairs: CryptoKeyPair[]","id: Uuid","activity: Create | Announce","updater: (\n existing: Create | Announce,\n ) => Create | Announce | undefined | Promise<Create | Announce | undefined>","options: RepositoryGetMessagesOptions","params: (number | string)[]","followRequestId: URL","follower: Actor","actorId: URL","followerId: URL","options: RepositoryGetFollowersOptions","follow: Follow","followeeId: URL","messageId: Uuid","voterId: URL","option: string","result: Record<string, number>"],"sources":["../src/mod.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 {\n ActorScopedRepository,\n type Repository,\n type RepositoryGetFollowersOptions,\n type RepositoryGetMessagesOptions,\n type Uuid,\n} from \"@fedify/botkit/repository\";\nimport { exportJwk, importJwk } from \"@fedify/fedify/sig\";\nimport {\n Activity,\n type Actor,\n Announce,\n Create,\n Follow,\n isActor,\n Object,\n} from \"@fedify/vocab\";\nimport { getLogger } from \"@logtape/logtape\";\nimport { DatabaseSync } from \"node:sqlite\";\n\nconst logger = getLogger([\"botkit\", \"sqlite\"]);\n\n/**\n * Options for creating a SQLite repository.\n * @since 0.3.0\n */\nexport interface SqliteRepositoryOptions {\n /**\n * The path to the SQLite database file.\n * If not provided, an in-memory database will be used.\n */\n readonly path?: string;\n\n /**\n * Whether to enable Write-Ahead Logging (WAL) mode.\n * @default true\n */\n readonly wal?: boolean;\n}\n\n/**\n * A repository for storing bot data using SQLite.\n * @since 0.3.0\n */\nexport class SqliteRepository implements Repository, Disposable {\n private readonly db: DatabaseSync;\n\n /**\n * Creates a new SQLite repository.\n * @param options The options for creating the repository.\n */\n constructor(options: SqliteRepositoryOptions = {}) {\n const { path = \":memory:\", wal = true } = options;\n\n this.db = new DatabaseSync(path);\n\n // Enable foreign key constraints\n this.db.exec(\"PRAGMA foreign_keys = ON;\");\n\n if (wal && path !== \":memory:\") {\n this.db.exec(\"PRAGMA journal_mode = WAL;\");\n }\n\n this.initializeTables();\n }\n\n [Symbol.dispose]() {\n this.close();\n }\n\n /**\n * Closes the database connection.\n */\n close(): void {\n this.db.close();\n }\n\n private tableExists(table: string): boolean {\n const stmt = this.db.prepare(`\n SELECT COUNT(*) AS count FROM sqlite_master\n WHERE type = 'table' AND name = ?\n `);\n const row = stmt.get(table) as { count: number };\n return row.count > 0;\n }\n\n private hasBotIdColumn(table: string): boolean {\n const stmt = this.db.prepare(`\n SELECT COUNT(*) AS count FROM pragma_table_info(?)\n WHERE name = 'bot_id'\n `);\n const row = stmt.get(table) as { count: number };\n return row.count > 0;\n }\n\n /**\n * Rebuilds tables created by \\@fedify/botkit-sqlite 0.4 or earlier, which\n * had no `bot_id` column, into the bot-scoped schema. Existing rows get\n * the empty-string bot ID; use {@link SqliteRepository.migrate} to assign\n * them to a bot actor identifier.\n */\n private rebuildLegacyTables(): void {\n const tables = [\n \"key_pairs\",\n \"messages\",\n \"followers\",\n \"follow_requests\",\n \"sent_follows\",\n \"followees\",\n \"poll_votes\",\n ].filter((table) => this.tableExists(table) && !this.hasBotIdColumn(table));\n if (tables.length < 1) return;\n logger.info(\n \"Rebuilding legacy tables without a bot_id column: {tables}.\",\n { tables },\n );\n // The marker lets migrate() distinguish rows carried over from a legacy\n // database (bot_id = '') from data legitimately stored under an\n // empty-string identifier:\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS botkit_metadata (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n )\n `);\n // SQLite cannot add a column to a composite primary key, so the tables\n // are rebuilt (create new, copy, drop, rename) in a single transaction.\n // Foreign key enforcement is turned off during the rebuild since\n // follow_requests references followers.\n this.db.exec(\"PRAGMA foreign_keys = OFF;\");\n this.db.exec(\"BEGIN TRANSACTION\");\n try {\n if (tables.includes(\"key_pairs\")) {\n // The primary key does not change; adding the column suffices:\n this.db.exec(`\n ALTER TABLE key_pairs ADD COLUMN bot_id TEXT NOT NULL DEFAULT ''\n `);\n }\n if (tables.includes(\"messages\")) {\n this.db.exec(`\n CREATE TABLE messages_new (\n bot_id TEXT NOT NULL,\n id TEXT NOT NULL,\n activity_json TEXT NOT NULL,\n published INTEGER,\n PRIMARY KEY (bot_id, id)\n )\n `);\n this.db.exec(`\n INSERT INTO messages_new (bot_id, id, activity_json, published)\n SELECT '', id, activity_json, published FROM messages\n `);\n this.db.exec(\"DROP TABLE messages\");\n this.db.exec(\"ALTER TABLE messages_new RENAME TO messages\");\n }\n if (tables.includes(\"followers\")) {\n this.db.exec(`\n CREATE TABLE followers_new (\n bot_id TEXT NOT NULL,\n follower_id TEXT NOT NULL,\n actor_json TEXT NOT NULL,\n PRIMARY KEY (bot_id, follower_id)\n )\n `);\n this.db.exec(`\n INSERT INTO followers_new (bot_id, follower_id, actor_json)\n SELECT '', follower_id, actor_json FROM followers\n `);\n this.db.exec(\"DROP TABLE followers\");\n this.db.exec(\"ALTER TABLE followers_new RENAME TO followers\");\n }\n if (tables.includes(\"follow_requests\")) {\n this.db.exec(`\n CREATE TABLE follow_requests_new (\n bot_id TEXT NOT NULL,\n follow_request_id TEXT NOT NULL,\n follower_id TEXT NOT NULL,\n PRIMARY KEY (bot_id, follow_request_id),\n FOREIGN KEY (bot_id, follower_id)\n REFERENCES followers(bot_id, follower_id)\n )\n `);\n this.db.exec(`\n INSERT INTO follow_requests_new\n (bot_id, follow_request_id, follower_id)\n SELECT '', follow_request_id, follower_id FROM follow_requests\n `);\n this.db.exec(\"DROP TABLE follow_requests\");\n this.db.exec(\n \"ALTER TABLE follow_requests_new RENAME TO follow_requests\",\n );\n }\n if (tables.includes(\"sent_follows\")) {\n this.db.exec(`\n CREATE TABLE sent_follows_new (\n bot_id TEXT NOT NULL,\n id TEXT NOT NULL,\n follow_json TEXT NOT NULL,\n PRIMARY KEY (bot_id, id)\n )\n `);\n this.db.exec(`\n INSERT INTO sent_follows_new (bot_id, id, follow_json)\n SELECT '', id, follow_json FROM sent_follows\n `);\n this.db.exec(\"DROP TABLE sent_follows\");\n this.db.exec(\"ALTER TABLE sent_follows_new RENAME TO sent_follows\");\n }\n if (tables.includes(\"followees\")) {\n this.db.exec(`\n CREATE TABLE followees_new (\n bot_id TEXT NOT NULL,\n followee_id TEXT NOT NULL,\n follow_json TEXT NOT NULL,\n PRIMARY KEY (bot_id, followee_id)\n )\n `);\n this.db.exec(`\n INSERT INTO followees_new (bot_id, followee_id, follow_json)\n SELECT '', followee_id, follow_json FROM followees\n `);\n this.db.exec(\"DROP TABLE followees\");\n this.db.exec(\"ALTER TABLE followees_new RENAME TO followees\");\n }\n if (tables.includes(\"poll_votes\")) {\n this.db.exec(`\n CREATE TABLE poll_votes_new (\n bot_id TEXT NOT NULL,\n message_id TEXT NOT NULL,\n voter_id TEXT NOT NULL,\n option TEXT NOT NULL,\n PRIMARY KEY (bot_id, message_id, voter_id, option)\n )\n `);\n this.db.exec(`\n INSERT INTO poll_votes_new (bot_id, message_id, voter_id, option)\n SELECT '', message_id, voter_id, option FROM poll_votes\n `);\n this.db.exec(\"DROP TABLE poll_votes\");\n this.db.exec(\"ALTER TABLE poll_votes_new RENAME TO poll_votes\");\n }\n this.db.exec(`\n INSERT OR REPLACE INTO botkit_metadata (key, value)\n VALUES ('legacy_data', '1')\n `);\n this.db.exec(\"COMMIT\");\n } catch (error) {\n this.db.exec(\"ROLLBACK\");\n this.db.exec(\"PRAGMA foreign_keys = ON;\");\n throw error;\n }\n this.db.exec(\"PRAGMA foreign_keys = ON;\");\n logger.info(\"Finished rebuilding legacy tables.\");\n }\n\n /**\n * Migrates data stored by \\@fedify/botkit-sqlite 0.4 or earlier, which was\n * not scoped by bot actor identifiers, so that it belongs to the given\n * identifier. Rows carried over from a legacy database have the\n * empty-string bot ID; this method assigns them to the identifier in\n * a single transaction. It only acts when the database was actually\n * rebuilt from a legacy schema, so data legitimately stored under an\n * empty-string identifier is never touched, and calling it again is\n * a no-op.\n * @param identifier The identifier of the bot actor that adopts the legacy\n * data.\n * @since 0.5.0\n */\n migrate(identifier: string): Promise<void> {\n if (!this.tableExists(\"botkit_metadata\")) return Promise.resolve();\n const marker = this.db.prepare(\n \"SELECT value FROM botkit_metadata WHERE key = 'legacy_data'\",\n ).get() as { value: string } | undefined;\n if (marker == null) return Promise.resolve();\n this.db.exec(\"BEGIN TRANSACTION\");\n // Updating followers and follow_requests rows in tandem temporarily\n // breaks the foreign key between them; defer the check to the commit:\n this.db.exec(\"PRAGMA defer_foreign_keys = ON\");\n try {\n for (\n const table of [\n \"key_pairs\",\n \"messages\",\n \"followers\",\n \"follow_requests\",\n \"sent_follows\",\n \"followees\",\n \"poll_votes\",\n ]\n ) {\n this.db.prepare(`UPDATE ${table} SET bot_id = ? WHERE bot_id = ''`)\n .run(identifier);\n }\n this.db.exec(\"DELETE FROM botkit_metadata WHERE key = 'legacy_data'\");\n this.db.exec(\"COMMIT\");\n } catch (error) {\n this.db.exec(\"ROLLBACK\");\n throw error;\n }\n return Promise.resolve();\n }\n\n private initializeTables(): void {\n this.rebuildLegacyTables();\n\n // Key pairs table\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS key_pairs (\n id INTEGER PRIMARY KEY,\n bot_id TEXT NOT NULL,\n private_key_jwk TEXT NOT NULL,\n public_key_jwk TEXT NOT NULL\n )\n `);\n\n // Create index on bot_id for efficient per-bot lookup\n this.db.exec(`\n CREATE INDEX IF NOT EXISTS idx_key_pairs_bot_id ON key_pairs(bot_id)\n `);\n\n // Messages table\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS messages (\n bot_id TEXT NOT NULL,\n id TEXT NOT NULL,\n activity_json TEXT NOT NULL,\n published INTEGER,\n PRIMARY KEY (bot_id, id)\n )\n `);\n\n // Create index on published timestamp for efficient ordering\n this.db.exec(`\n CREATE INDEX IF NOT EXISTS idx_messages_bot_published\n ON messages(bot_id, published)\n `);\n\n // Followers table\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS followers (\n bot_id TEXT NOT NULL,\n follower_id TEXT NOT NULL,\n actor_json TEXT NOT NULL,\n PRIMARY KEY (bot_id, follower_id)\n )\n `);\n\n // Follow requests mapping table\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS follow_requests (\n bot_id TEXT NOT NULL,\n follow_request_id TEXT NOT NULL,\n follower_id TEXT NOT NULL,\n PRIMARY KEY (bot_id, follow_request_id),\n FOREIGN KEY (bot_id, follower_id)\n REFERENCES followers(bot_id, follower_id)\n )\n `);\n\n // Sent follows table\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS sent_follows (\n bot_id TEXT NOT NULL,\n id TEXT NOT NULL,\n follow_json TEXT NOT NULL,\n PRIMARY KEY (bot_id, id)\n )\n `);\n\n // Followees table\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS followees (\n bot_id TEXT NOT NULL,\n followee_id TEXT NOT NULL,\n follow_json TEXT NOT NULL,\n PRIMARY KEY (bot_id, followee_id)\n )\n `);\n\n // Create index for reverse lookup of bots following an actor\n this.db.exec(`\n CREATE INDEX IF NOT EXISTS idx_followees_followee_id\n ON followees(followee_id)\n `);\n\n // Poll votes table\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS poll_votes (\n bot_id TEXT NOT NULL,\n message_id TEXT NOT NULL,\n voter_id TEXT NOT NULL,\n option TEXT NOT NULL,\n PRIMARY KEY (bot_id, message_id, voter_id, option)\n )\n `);\n\n // Create index for efficient vote counting\n this.db.exec(`\n CREATE INDEX IF NOT EXISTS idx_poll_votes_bot_message_option\n ON poll_votes(bot_id, message_id, option)\n `);\n }\n\n async setKeyPairs(\n identifier: string,\n keyPairs: CryptoKeyPair[],\n ): Promise<void> {\n const deleteStmt = this.db.prepare(\n \"DELETE FROM key_pairs WHERE bot_id = ?\",\n );\n const insertStmt = this.db.prepare(`\n INSERT INTO key_pairs (bot_id, private_key_jwk, public_key_jwk)\n VALUES (?, ?, ?)\n `);\n\n this.db.exec(\"BEGIN TRANSACTION\");\n try {\n deleteStmt.run(identifier);\n\n for (const keyPair of keyPairs) {\n const privateJwk = await exportJwk(keyPair.privateKey);\n const publicJwk = await exportJwk(keyPair.publicKey);\n insertStmt.run(\n identifier,\n JSON.stringify(privateJwk),\n JSON.stringify(publicJwk),\n );\n }\n\n this.db.exec(\"COMMIT\");\n } catch (error) {\n this.db.exec(\"ROLLBACK\");\n throw error;\n }\n }\n\n async getKeyPairs(identifier: string): Promise<CryptoKeyPair[] | undefined> {\n const stmt = this.db.prepare(`\n SELECT private_key_jwk, public_key_jwk FROM key_pairs\n WHERE bot_id = ? ORDER BY id\n `);\n const rows = stmt.all(identifier) as Array<{\n private_key_jwk: string;\n public_key_jwk: string;\n }>;\n\n if (rows.length === 0) return undefined;\n\n const keyPairs: CryptoKeyPair[] = [];\n for (const row of rows) {\n const privateJwk = JSON.parse(row.private_key_jwk);\n const publicJwk = JSON.parse(row.public_key_jwk);\n\n keyPairs.push({\n privateKey: await importJwk(privateJwk, \"private\"),\n publicKey: await importJwk(publicJwk, \"public\"),\n });\n }\n\n return keyPairs;\n }\n\n async addMessage(\n identifier: string,\n id: Uuid,\n activity: Create | Announce,\n ): Promise<void> {\n const stmt = this.db.prepare(`\n INSERT INTO messages (bot_id, id, activity_json, published)\n VALUES (?, ?, ?, ?)\n `);\n\n const activityJson = JSON.stringify(\n await activity.toJsonLd({ format: \"compact\" }),\n );\n const published = activity.published?.epochMilliseconds ?? null;\n\n stmt.run(identifier, id, activityJson, published);\n }\n\n async updateMessage(\n identifier: string,\n id: Uuid,\n updater: (\n existing: Create | Announce,\n ) => Create | Announce | undefined | Promise<Create | Announce | undefined>,\n ): Promise<boolean> {\n const selectStmt = this.db.prepare(`\n SELECT activity_json FROM messages WHERE bot_id = ? AND id = ?\n `);\n const row = selectStmt.get(identifier, id) as\n | { activity_json: string }\n | undefined;\n\n if (!row) return false;\n\n const activityData = JSON.parse(row.activity_json);\n const activity = await Activity.fromJsonLd(activityData);\n\n if (!(activity instanceof Create || activity instanceof Announce)) {\n return false;\n }\n\n const newActivity = await updater(activity);\n if (newActivity == null) return false;\n\n const updateStmt = this.db.prepare(`\n UPDATE messages\n SET activity_json = ?, published = ?\n WHERE bot_id = ? AND id = ?\n `);\n\n const newActivityJson = JSON.stringify(\n await newActivity.toJsonLd({ format: \"compact\" }),\n );\n const published = newActivity.published?.epochMilliseconds ?? null;\n\n updateStmt.run(newActivityJson, published, identifier, id);\n return true;\n }\n\n async removeMessage(\n identifier: string,\n id: Uuid,\n ): Promise<Create | Announce | undefined> {\n const selectStmt = this.db.prepare(`\n SELECT activity_json FROM messages WHERE bot_id = ? AND id = ?\n `);\n const row = selectStmt.get(identifier, id) as\n | { activity_json: string }\n | undefined;\n\n if (!row) return undefined;\n\n const deleteStmt = this.db.prepare(`\n DELETE FROM messages WHERE bot_id = ? AND id = ?\n `);\n deleteStmt.run(identifier, id);\n\n try {\n const activityData = JSON.parse(row.activity_json);\n const activity = await Activity.fromJsonLd(activityData);\n\n if (activity instanceof Create || activity instanceof Announce) {\n return activity;\n }\n } catch (error) {\n logger.warn(\"Failed to parse removed message activity\", { id, error });\n }\n\n return undefined;\n }\n\n async *getMessages(\n identifier: string,\n options: RepositoryGetMessagesOptions = {},\n ): AsyncIterable<Create | Announce> {\n const { order = \"newest\", until, since, limit } = options;\n\n let sql = \"SELECT activity_json FROM messages WHERE bot_id = ?\";\n const params: (number | string)[] = [identifier];\n\n if (since != null) {\n sql += \" AND published >= ?\";\n params.push(since.epochMilliseconds);\n }\n\n if (until != null) {\n sql += \" AND published <= ?\";\n params.push(until.epochMilliseconds);\n }\n\n sql += order === \"oldest\"\n ? \" ORDER BY published ASC\"\n : \" ORDER BY published DESC\";\n\n if (limit != null) {\n sql += \" LIMIT ?\";\n params.push(limit);\n }\n\n const stmt = this.db.prepare(sql);\n const rows = stmt.all(...params) as Array<{ activity_json: string }>;\n\n for (const row of rows) {\n try {\n const activityData = JSON.parse(row.activity_json);\n const activity = await Activity.fromJsonLd(activityData);\n\n if (activity instanceof Create || activity instanceof Announce) {\n yield activity;\n }\n } catch (error) {\n logger.warn(\"Failed to parse message activity\", { error });\n continue;\n }\n }\n }\n\n async getMessage(\n identifier: string,\n id: Uuid,\n ): Promise<Create | Announce | undefined> {\n const stmt = this.db.prepare(`\n SELECT activity_json FROM messages WHERE bot_id = ? AND id = ?\n `);\n const row = stmt.get(identifier, id) as\n | { activity_json: string }\n | undefined;\n\n if (!row) return undefined;\n\n try {\n const activityData = JSON.parse(row.activity_json);\n const activity = await Activity.fromJsonLd(activityData);\n\n if (activity instanceof Create || activity instanceof Announce) {\n return activity;\n }\n } catch (error) {\n logger.warn(\"Failed to parse message activity\", { id, error });\n }\n\n return undefined;\n }\n\n countMessages(identifier: string): Promise<number> {\n const stmt = this.db.prepare(\n \"SELECT COUNT(*) as count FROM messages WHERE bot_id = ?\",\n );\n const row = stmt.get(identifier) as { count: number };\n return Promise.resolve(row.count);\n }\n\n async addFollower(\n identifier: string,\n followRequestId: URL,\n follower: Actor,\n ): Promise<void> {\n if (follower.id == null) {\n throw new TypeError(\"The follower ID is missing.\");\n }\n\n const followerJson = JSON.stringify(\n await follower.toJsonLd({ format: \"compact\" }),\n );\n\n const insertFollowerStmt = this.db.prepare(`\n INSERT OR REPLACE INTO followers (bot_id, follower_id, actor_json)\n VALUES (?, ?, ?)\n `);\n\n const insertRequestStmt = this.db.prepare(`\n INSERT OR REPLACE INTO follow_requests\n (bot_id, follow_request_id, follower_id)\n VALUES (?, ?, ?)\n `);\n\n this.db.exec(\"BEGIN TRANSACTION\");\n try {\n insertFollowerStmt.run(identifier, follower.id.href, followerJson);\n insertRequestStmt.run(\n identifier,\n followRequestId.href,\n follower.id.href,\n );\n this.db.exec(\"COMMIT\");\n } catch (error) {\n this.db.exec(\"ROLLBACK\");\n throw error;\n }\n }\n\n async removeFollower(\n identifier: string,\n followRequestId: URL,\n actorId: URL,\n ): Promise<Actor | undefined> {\n // Check if the follow request exists and matches the actor\n const checkStmt = this.db.prepare(`\n SELECT fr.follower_id, f.actor_json\n FROM follow_requests fr\n JOIN followers f\n ON fr.bot_id = f.bot_id AND fr.follower_id = f.follower_id\n WHERE fr.bot_id = ? AND fr.follow_request_id = ? AND fr.follower_id = ?\n `);\n\n const row = checkStmt.get(\n identifier,\n followRequestId.href,\n actorId.href,\n ) as\n | {\n follower_id: string;\n actor_json: string;\n }\n | undefined;\n\n if (!row) return undefined;\n\n // Remove the follower and follow request\n const deleteRequestStmt = this.db.prepare(`\n DELETE FROM follow_requests WHERE bot_id = ? AND follow_request_id = ?\n `);\n\n const deleteFollowerStmt = this.db.prepare(`\n DELETE FROM followers WHERE bot_id = ? AND follower_id = ?\n `);\n\n this.db.exec(\"BEGIN TRANSACTION\");\n try {\n deleteRequestStmt.run(identifier, followRequestId.href);\n deleteFollowerStmt.run(identifier, actorId.href);\n this.db.exec(\"COMMIT\");\n } catch (error) {\n this.db.exec(\"ROLLBACK\");\n throw error;\n }\n\n try {\n const actorData = JSON.parse(row.actor_json);\n const actor = await Object.fromJsonLd(actorData);\n\n if (isActor(actor)) {\n return actor;\n }\n } catch (error) {\n logger.warn(\"Failed to parse removed follower actor\", { error });\n }\n\n return undefined;\n }\n\n hasFollower(identifier: string, followerId: URL): Promise<boolean> {\n const stmt = this.db.prepare(`\n SELECT 1 FROM followers WHERE bot_id = ? AND follower_id = ?\n `);\n const row = stmt.get(identifier, followerId.href);\n return Promise.resolve(row != null);\n }\n\n async *getFollowers(\n identifier: string,\n options: RepositoryGetFollowersOptions = {},\n ): AsyncIterable<Actor> {\n const { offset = 0, limit } = options;\n\n let sql =\n \"SELECT actor_json FROM followers WHERE bot_id = ? ORDER BY follower_id\";\n const params: (number | string)[] = [identifier];\n\n if (limit != null) {\n sql += \" LIMIT ? OFFSET ?\";\n params.push(limit, offset);\n } else if (offset > 0) {\n sql += \" LIMIT -1 OFFSET ?\";\n params.push(offset);\n }\n\n const stmt = this.db.prepare(sql);\n const rows = stmt.all(...params) as { actor_json: string }[];\n\n for (const row of rows) {\n try {\n const actorData = JSON.parse(row.actor_json);\n const actor = await Object.fromJsonLd(actorData);\n\n if (isActor(actor)) {\n yield actor;\n }\n } catch (error) {\n logger.warn(\"Failed to parse follower actor\", { error });\n continue;\n }\n }\n }\n\n countFollowers(identifier: string): Promise<number> {\n const stmt = this.db.prepare(\n \"SELECT COUNT(*) as count FROM followers WHERE bot_id = ?\",\n );\n const row = stmt.get(identifier) as { count: number };\n return Promise.resolve(row.count);\n }\n\n async addSentFollow(\n identifier: string,\n id: Uuid,\n follow: Follow,\n ): Promise<void> {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO sent_follows (bot_id, id, follow_json)\n VALUES (?, ?, ?)\n `);\n\n const followJson = JSON.stringify(\n await follow.toJsonLd({ format: \"compact\" }),\n );\n\n stmt.run(identifier, id, followJson);\n }\n\n async removeSentFollow(\n identifier: string,\n id: Uuid,\n ): Promise<Follow | undefined> {\n const follow = await this.getSentFollow(identifier, id);\n if (follow == null) return undefined;\n\n const stmt = this.db.prepare(\n \"DELETE FROM sent_follows WHERE bot_id = ? AND id = ?\",\n );\n stmt.run(identifier, id);\n\n return follow;\n }\n\n async getSentFollow(\n identifier: string,\n id: Uuid,\n ): Promise<Follow | undefined> {\n const stmt = this.db.prepare(`\n SELECT follow_json FROM sent_follows WHERE bot_id = ? AND id = ?\n `);\n const row = stmt.get(identifier, id) as\n | { follow_json: string }\n | undefined;\n\n if (!row) return undefined;\n\n try {\n const followData = JSON.parse(row.follow_json);\n return await Follow.fromJsonLd(followData);\n } catch (error) {\n logger.warn(\"Failed to parse sent follow activity\", { id, error });\n return undefined;\n }\n }\n\n async addFollowee(\n identifier: string,\n followeeId: URL,\n follow: Follow,\n ): Promise<void> {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO followees (bot_id, followee_id, follow_json)\n VALUES (?, ?, ?)\n `);\n\n const followJson = JSON.stringify(\n await follow.toJsonLd({ format: \"compact\" }),\n );\n\n stmt.run(identifier, followeeId.href, followJson);\n }\n\n async removeFollowee(\n identifier: string,\n followeeId: URL,\n ): Promise<Follow | undefined> {\n const follow = await this.getFollowee(identifier, followeeId);\n if (follow == null) return undefined;\n\n const stmt = this.db.prepare(\n \"DELETE FROM followees WHERE bot_id = ? AND followee_id = ?\",\n );\n stmt.run(identifier, followeeId.href);\n\n return follow;\n }\n\n async getFollowee(\n identifier: string,\n followeeId: URL,\n ): Promise<Follow | undefined> {\n const stmt = this.db.prepare(`\n SELECT follow_json FROM followees WHERE bot_id = ? AND followee_id = ?\n `);\n const row = stmt.get(identifier, followeeId.href) as\n | { follow_json: string }\n | undefined;\n\n if (!row) return undefined;\n\n try {\n const followData = JSON.parse(row.follow_json);\n return await Follow.fromJsonLd(followData);\n } catch (error) {\n logger.warn(\"Failed to parse followee activity\", {\n followeeId: followeeId.href,\n error,\n });\n return undefined;\n }\n }\n\n async *findFollowedBots(followeeId: URL): AsyncIterable<string> {\n const stmt = this.db.prepare(`\n SELECT bot_id FROM followees WHERE followee_id = ? ORDER BY bot_id\n `);\n const rows = stmt.all(followeeId.href) as { bot_id: string }[];\n for (const row of rows) yield row.bot_id;\n }\n\n vote(\n identifier: string,\n messageId: Uuid,\n voterId: URL,\n option: string,\n ): Promise<void> {\n const stmt = this.db.prepare(`\n INSERT OR IGNORE INTO poll_votes (bot_id, message_id, voter_id, option)\n VALUES (?, ?, ?, ?)\n `);\n\n stmt.run(identifier, messageId, voterId.href, option);\n return Promise.resolve();\n }\n\n countVoters(identifier: string, messageId: Uuid): Promise<number> {\n const stmt = this.db.prepare(`\n SELECT COUNT(DISTINCT voter_id) as count\n FROM poll_votes\n WHERE bot_id = ? AND message_id = ?\n `);\n const row = stmt.get(identifier, messageId) as { count: number };\n return Promise.resolve(row.count);\n }\n\n countVotes(\n identifier: string,\n messageId: Uuid,\n ): Promise<Readonly<Record<string, number>>> {\n const stmt = this.db.prepare(`\n SELECT option, COUNT(*) as count\n FROM poll_votes\n WHERE bot_id = ? AND message_id = ?\n GROUP BY option\n `);\n const rows = stmt.all(identifier, messageId) as Array<{\n option: string;\n count: number;\n }>;\n\n const result: Record<string, number> = {};\n for (const row of rows) {\n result[row.option] = row.count;\n }\n\n return Promise.resolve(result);\n }\n\n forIdentifier(identifier: string): ActorScopedRepository {\n return new ActorScopedRepository(this, identifier);\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/botkit-sqlite",
|
|
3
|
-
"version": "0.5.0-dev.
|
|
3
|
+
"version": "0.5.0-dev.225",
|
|
4
4
|
"description": "SQLite-based repository for BotKit",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"author": {
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"README.md"
|
|
44
44
|
],
|
|
45
45
|
"peerDependencies": {
|
|
46
|
-
"@fedify/botkit": "0.5.0-dev.
|
|
46
|
+
"@fedify/botkit": "^0.5.0-dev.225+cfc4181c"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@fedify/fedify": "~2.1.15",
|