@fedify/botkit 0.4.0-dev.192 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bot-impl.js.map +1 -1
- package/dist/bot-impl.test.js.map +1 -1
- package/dist/bot.js.map +1 -1
- package/dist/bot.test.js.map +1 -1
- package/dist/components/FollowButton.d.ts +2 -2
- package/dist/components/Follower.d.ts +2 -2
- package/dist/components/Follower.js.map +1 -1
- package/dist/components/Layout.js.map +1 -1
- package/dist/components/Message.d.ts +2 -2
- package/dist/components/Message.js.map +1 -1
- package/dist/components/Message.test.js.map +1 -1
- package/dist/deno.js +1 -1
- package/dist/deno.js.map +1 -1
- package/dist/emoji.js.map +1 -1
- package/dist/emoji.test.js.map +1 -1
- package/dist/follow-impl.js.map +1 -1
- package/dist/follow-impl.test.js.map +1 -1
- package/dist/message-impl.js.map +1 -1
- package/dist/message-impl.test.js.map +1 -1
- package/dist/pages.js.map +1 -1
- package/dist/repository.js.map +1 -1
- package/dist/repository.test.js.map +1 -1
- package/dist/session-impl.js.map +1 -1
- package/dist/session-impl.test.js.map +1 -1
- package/dist/text.js.map +1 -1
- package/dist/text.test.js.map +1 -1
- package/package.json +1 -1
package/dist/repository.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repository.js","names":["kv: KvStore","prefixes?: KvStoreRepositoryPrefixes","keyPairs: CryptoKeyPair[]","pairs: KeyPair[]","pair: KeyPair","id: Uuid","activity: Create | Announce","messageKey: KvKey","lockKey: KvKey","listKey: KvKey","updater: (\n existing: Create | Announce,\n ) => Create | Announce | undefined | Promise<Create | Announce | undefined>","kvKey: KvKey","Create","Announce","options: RepositoryGetMessagesOptions","activity: Activity","followRequestId: URL","follower: Actor","followerKey: KvKey","followRequestKey: KvKey","actorId: URL","follower: Object","followerId: URL","options: RepositoryGetFollowersOptions","actor: Object","follow: Follow","followeeId: URL","messageId: Uuid","voterId: URL","option: string","key: KvKey","optionsKey: KvKey","result: Record<string, number>","uuid: string","followId: URL","counts: Record<string, number>","underlying: Repository","cache?: MemoryRepository","options?: RepositoryGetMessagesOptions","options?: RepositoryGetFollowersOptions"],"sources":["../src/repository.ts"],"sourcesContent":["// BotKit by Fedify: A framework for creating ActivityPub bots\n// Copyright (C) 2025 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 { KvKey, KvStore } from \"@fedify/fedify/federation\";\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\";\nexport type { KvKey, KvStore } from \"@fedify/fedify/federation\";\nexport { Announce, Create } from \"@fedify/vocab\";\n\nconst logger = getLogger([\"botkit\", \"repository\"]);\n\n/**\n * A UUID (universally unique identifier).\n * @since 0.3.0\n */\nexport type Uuid = ReturnType<typeof crypto.randomUUID>;\n\n/**\n * A repository for storing bot data.\n * @since 0.3.0\n */\nexport interface Repository {\n /**\n * Sets the key pairs of the bot actor.\n * @param keyPairs The key pairs to set.\n */\n setKeyPairs(keyPairs: CryptoKeyPair[]): Promise<void>;\n\n /**\n * Gets the key pairs of the bot actor.\n * @returns The key pairs of the bot actor. If the key pairs do not exist,\n * `undefined` will be returned.\n */\n getKeyPairs(): Promise<CryptoKeyPair[] | undefined>;\n\n /**\n * Adds a message to the repository.\n * @param id The UUID of the message.\n * @param activity The activity to add.\n */\n addMessage(id: Uuid, activity: Create | Announce): Promise<void>;\n\n /**\n * Updates a message in the repository.\n * @param id The UUID of the message.\n * @param updater The function to update the message. The function will be\n * called with the existing message, and the return value will\n * be the new message. If the function returns a promise, the\n * promise will be awaited. If the function returns either\n * `undefined` or a promise that resolves to `undefined`,\n * the message will not be updated. If the message does not\n * exist, the updater will not be called.\n * @returns `true` if the message was updated, `false` if the message does not\n * exist.\n */\n updateMessage(\n id: Uuid,\n updater: (\n existing: Create | Announce,\n ) => Create | Announce | undefined | Promise<Create | Announce | undefined>,\n ): Promise<boolean>;\n\n /**\n * Removes a message from the repository.\n * @param id The UUID of the message to remove.\n * @returns The removed activity. If the message does not exist, `undefined`\n * will be returned.\n */\n removeMessage(id: Uuid): Promise<Create | Announce | undefined>;\n\n /**\n * Gets messages from the repository.\n * @param options The options for getting messages.\n * @returns An async iterable of message activities.\n */\n getMessages(\n options?: RepositoryGetMessagesOptions,\n ): AsyncIterable<Create | Announce>;\n\n /**\n * Gets a message from the repository.\n * @param id The UUID of the message to get.\n * @returns The message activity, or `undefined` if the message does not\n * exist.\n */\n getMessage(id: Uuid): Promise<Create | Announce | undefined>;\n\n /**\n * Counts the number of messages in the repository.\n * @returns The number of messages in the repository.\n */\n countMessages(): Promise<number>;\n\n /**\n * Adds a follower to the repository.\n * @param followId The URL of the follow request.\n * @param follower The actor who follows the bot.\n */\n addFollower(followId: URL, follower: Actor): Promise<void>;\n\n /**\n * Removes a follower from the repository.\n * @param followId The URL of the follow request.\n * @param followerId The ID of the actor to remove.\n * @returns The removed actor. If the follower does not exist or the follow\n * request is not about the follower, `undefined` will be returned.\n */\n removeFollower(followId: URL, followerId: URL): Promise<Actor | undefined>;\n\n /**\n * Checks if the repository has a follower.\n * @param followerId The ID of the follower to check.\n * @returns `true` if the repository has the follower, `false` otherwise.\n */\n hasFollower(followerId: URL): Promise<boolean>;\n\n /**\n * Gets followers from the repository.\n * @param options The options for getting followers.\n * @returns An async iterable of actors who follow the bot.\n */\n getFollowers(options?: RepositoryGetFollowersOptions): AsyncIterable<Actor>;\n\n /**\n * Counts the number of followers in the repository.\n * @returns The number of followers in the repository.\n */\n countFollowers(): Promise<number>;\n\n /**\n * Adds a sent follow request to the repository.\n * @param id The UUID of the follow request.\n * @param follow The follow activity to add.\n */\n addSentFollow(id: Uuid, follow: Follow): Promise<void>;\n\n /**\n * Removes a sent follow request from the repository.\n * @param id The UUID of the follow request to remove.\n * @returns The removed follow activity. If the follow request does not\n * exist, `undefined` will be returned.\n */\n removeSentFollow(id: Uuid): Promise<Follow | undefined>;\n\n /**\n * Gets a sent follow request from the repository.\n * @param id The UUID of the follow request to get.\n * @returns The `Follow` activity, or `undefined` if the follow request does\n * not exist.\n */\n getSentFollow(id: Uuid): Promise<Follow | undefined>;\n\n /**\n * Adds a followee to the repository.\n * @param followeeId The ID of the followee to add.\n * @param follow The follow activity to add.\n */\n addFollowee(followeeId: URL, follow: Follow): Promise<void>;\n\n /**\n * Removes a followee from the repository.\n * @param followeeId The ID of the followee to remove.\n * @returns The `Follow` activity that was removed. If the followee does not\n * exist, `undefined` will be returned.\n */\n removeFollowee(followeeId: URL): Promise<Follow | undefined>;\n\n /**\n * Gets a followee from the repository.\n * @param followeeId The ID of the followee to get.\n * @returns The `Follow` activity, or `undefined` if the followee does not\n * exist.\n */\n getFollowee(followeeId: URL): Promise<Follow | undefined>;\n\n /**\n * Records a vote in a poll. If the same voter had already voted for the\n * same option in a poll, the vote will be silently ignored.\n * @param messageId The UUID of the poll message to vote on.\n * @param voterId The ID of the voter. It should be a URL of the actor who is\n * voting.\n * @param option The option that the voter is voting for. It should be one of\n * the options in the poll. If the poll allows multiple\n * selections, this should be a single option that the voter is\n * voting for, which is one of multiple calls to this method.\n * @since 0.3.0\n */\n vote(messageId: Uuid, voterId: URL, option: string): Promise<void>;\n\n /**\n * Counts the number of voters in a poll. Even if the poll allows multiple\n * selections, each voter is counted only once.\n * @param messageId The UUID of the poll message to count voters for.\n * @returns The number of voters in the poll. If the poll does not exist,\n * 0 will be returned.\n * @since 0.3.0\n */\n countVoters(messageId: Uuid): Promise<number>;\n\n /**\n * Counts the votes for each option in a poll. If the poll allows multiple\n * selections, each option is counted separately, and the same voter can\n * vote for multiple options.\n * @param messageId The UUID of the poll message to count votes for.\n * @returns A record where the keys are the options and the values are\n * the number of votes for each option. If the poll does not exist,\n * an empty record will be returned. Some options may not be\n * present in the record if no votes were cast for them.\n * @since 0.3.0\n */\n countVotes(messageId: Uuid): Promise<Readonly<Record<string, number>>>;\n}\n\n/**\n * Options for getting messages from the repository.\n * @since 0.3.0\n */\nexport interface RepositoryGetMessagesOptions {\n /**\n * The order of the messages. If omitted, `\"newest\"` will be used.\n * @default `\"newest\"`\n */\n readonly order?: \"oldest\" | \"newest\";\n\n /**\n * The timestamp to get messages created at or before this time.\n * If omitted, no limit will be applied.\n */\n readonly until?: Temporal.Instant;\n\n /**\n * The timestamp to get messages created at or after this time.\n * If omitted, no limit will be applied.\n */\n readonly since?: Temporal.Instant;\n\n /**\n * The maximum number of messages to get. If omitted, no limit will be\n * applied.\n */\n readonly limit?: number;\n}\n\n/**\n * Options for getting followers from the repository.\n * @since 0.3.0\n */\nexport interface RepositoryGetFollowersOptions {\n /**\n * The offset of the followers to get. If omitted, 0 will be used.\n * @default `0`\n */\n readonly offset?: number;\n\n /**\n * The limit of the followers to get. If omitted, no limit will be applied.\n */\n readonly limit?: number;\n}\n\n/**\n * The prefixes for key-value store keys used by the bot.\n * @since 0.3.0\n */\nexport interface KvStoreRepositoryPrefixes {\n /**\n * The key prefix used for storing the key pairs of the bot actor.\n * @default `[\"_botkit\", \"keyPairs\"]`\n */\n readonly keyPairs: KvKey;\n\n /**\n * The key prefix used for storing published messages.\n * @default `[\"_botkit\", \"messages\"]`\n */\n readonly messages: KvKey;\n\n /**\n * The key prefix used for storing followers.\n * @default `[\"_botkit\", \"followers\"]`\n */\n readonly followers: KvKey;\n\n /**\n * The key prefix used for storing incoming follow requests.\n * @default `[\"_botkit\", \"followRequests\"]`\n */\n readonly followRequests: KvKey;\n\n /**\n * The key prefix used for storing followees.\n * @default `[\"_botkit\", \"followees\"]`\n */\n readonly followees: KvKey;\n\n /**\n * The key prefix used for storing outgoing follow requests.\n * @default `[\"_botkit\", \"follows\"]`\n */\n readonly follows: KvKey;\n\n /**\n * The key prefix used for storing poll votes.\n * @default `[\"_botkit\", \"polls\"]`\n * @since 0.3.0\n */\n readonly polls: KvKey;\n}\n\n/**\n * A repository for storing bot data using a key-value store.\n */\nexport class KvRepository implements Repository {\n readonly kv: KvStore;\n readonly prefixes: KvStoreRepositoryPrefixes;\n\n /**\n * Creates a new key-value store repository.\n * @param kv The key-value store to use.\n * @param prefixes The prefixes for key-value store keys.\n */\n constructor(kv: KvStore, prefixes?: KvStoreRepositoryPrefixes) {\n if (kv.cas == null) {\n logger.warn(\n \"The given KvStore {kv} does not support CAS operations. \" +\n \"This may cause issues with concurrent updates.\",\n { kv },\n );\n }\n this.kv = kv;\n this.prefixes = {\n keyPairs: [\"_botkit\", \"keyPairs\"],\n messages: [\"_botkit\", \"messages\"],\n followers: [\"_botkit\", \"followers\"],\n followRequests: [\"_botkit\", \"followRequests\"],\n followees: [\"_botkit\", \"followees\"],\n follows: [\"_botkit\", \"follows\"],\n polls: [\"_botkit\", \"polls\"],\n ...prefixes ?? {},\n };\n }\n\n async setKeyPairs(keyPairs: CryptoKeyPair[]): Promise<void> {\n const pairs: KeyPair[] = [];\n for (const keyPair of keyPairs) {\n const pair: KeyPair = {\n private: await exportJwk(keyPair.privateKey),\n public: await exportJwk(keyPair.publicKey),\n };\n pairs.push(pair);\n }\n await this.kv.set(this.prefixes.keyPairs, pairs);\n }\n\n async getKeyPairs(): Promise<CryptoKeyPair[] | undefined> {\n const keyPairs = await this.kv.get<KeyPair[]>(this.prefixes.keyPairs);\n if (keyPairs == null) return undefined;\n const promises = keyPairs.map(async (pair) => ({\n privateKey: await importJwk(pair.private, \"private\"),\n publicKey: await importJwk(pair.public, \"public\"),\n }));\n return await Promise.all(promises);\n }\n\n async addMessage(id: Uuid, activity: Create | Announce): Promise<void> {\n const messageKey: KvKey = [...this.prefixes.messages, id];\n await this.kv.set(\n messageKey,\n await activity.toJsonLd({ format: \"compact\" }),\n );\n const lockKey: KvKey = [...this.prefixes.messages, \"lock\"];\n const listKey: KvKey = this.prefixes.messages;\n do {\n await this.kv.set(lockKey, id);\n const set = new Set(await this.kv.get<string[]>(listKey) ?? []);\n set.add(id);\n const list = [...set];\n list.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);\n await this.kv.set(listKey, list);\n } while (await this.kv.get(lockKey) !== id);\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 kvKey: KvKey = [...this.prefixes.messages, id];\n const createJson = await this.kv.get(kvKey);\n if (createJson == null) return false;\n const activity = await Activity.fromJsonLd(createJson);\n if (!(activity instanceof Create || activity instanceof Announce)) {\n return false;\n }\n const newActivity = await updater(activity);\n if (newActivity == null) return false;\n await this.kv.set(\n kvKey,\n await newActivity.toJsonLd({ format: \"compact\" }),\n );\n return true;\n }\n\n async removeMessage(id: Uuid): Promise<Create | Announce | undefined> {\n const listKey: KvKey = this.prefixes.messages;\n const lockKey: KvKey = [...listKey, \"lock\"];\n const lockId = `${id}:delete`;\n do {\n await this.kv.set(lockKey, lockId);\n const set = new Set(await this.kv.get<string[]>(listKey) ?? []);\n set.delete(id);\n const list = [...set];\n list.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);\n await this.kv.set(listKey, list);\n } while (await this.kv.get(lockKey) !== lockId);\n const messageKey: KvKey = [...listKey, id];\n const activityJson = await this.kv.get(messageKey);\n if (activityJson == null) return;\n await this.kv.delete(messageKey);\n const activity = await Activity.fromJsonLd(activityJson);\n if (activity instanceof Create || activity instanceof Announce) {\n return activity;\n }\n return undefined;\n }\n\n async *getMessages(\n options: RepositoryGetMessagesOptions = {},\n ): AsyncIterable<Create | Announce> {\n const { order, until, since, limit } = options;\n const untilTs = until == null ? null : until.epochMilliseconds;\n const sinceTs = since == null ? null : since.epochMilliseconds;\n let messageIds = await this.kv.get<string[]>(this.prefixes.messages) ?? [];\n if (sinceTs != null) {\n const offset = messageIds.findIndex((id) =>\n extractTimestamp(id) >= sinceTs\n );\n messageIds = messageIds.slice(offset);\n }\n if (untilTs != null) {\n const offset = messageIds.findLastIndex((id) =>\n extractTimestamp(id) <= untilTs\n );\n messageIds = messageIds.slice(0, offset + 1);\n }\n if (order == null || order === \"newest\") {\n messageIds = messageIds.toReversed();\n }\n if (limit != null) {\n messageIds = messageIds.slice(0, limit);\n }\n for (const id of messageIds) {\n const messageJson = await this.kv.get([...this.prefixes.messages, id]);\n if (messageJson == null) continue;\n try {\n const activity = await Activity.fromJsonLd(messageJson);\n if (activity instanceof Create || activity instanceof Announce) {\n yield activity;\n }\n } catch {\n continue;\n }\n }\n }\n\n async getMessage(id: Uuid): Promise<Create | Announce | undefined> {\n const json = await this.kv.get([...this.prefixes.messages, id]);\n if (json == null) return undefined;\n let activity: Activity;\n try {\n activity = await Activity.fromJsonLd(json);\n } catch (e) {\n if (e instanceof TypeError) return undefined;\n throw e;\n }\n if (activity instanceof Create || activity instanceof Announce) {\n return activity;\n }\n return undefined;\n }\n\n async countMessages(): Promise<number> {\n const messageIds = await this.kv.get<string[]>(this.prefixes.messages) ??\n [];\n return messageIds.length;\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 const followerKey: KvKey = [...this.prefixes.followers, follower.id.href];\n await this.kv.set(\n followerKey,\n await follower.toJsonLd({ format: \"compact\" }),\n );\n const lockKey: KvKey = [...this.prefixes.followers, \"lock\"];\n const listKey: KvKey = this.prefixes.followers;\n do {\n await this.kv.set(lockKey, follower.id.href);\n const list = await this.kv.get<string[]>(listKey) ?? [];\n if (!list.includes(follower.id.href)) list.push(follower.id.href);\n await this.kv.set(listKey, list);\n } while (await this.kv.get(lockKey) !== follower.id.href);\n const followRequestKey: KvKey = [\n ...this.prefixes.followRequests,\n followRequestId.href,\n ];\n await this.kv.set(followRequestKey, follower.id.href);\n }\n\n async removeFollower(\n followRequestId: URL,\n actorId: URL,\n ): Promise<Actor | undefined> {\n const followRequestKey: KvKey = [\n ...this.prefixes.followRequests,\n followRequestId.href,\n ];\n const followerId = await this.kv.get<string>(followRequestKey);\n if (followerId == null) return undefined;\n const followerKey: KvKey = [...this.prefixes.followers, followerId];\n if (followerId !== actorId.href) return undefined;\n const followerJson = await this.kv.get(followerKey);\n if (followerJson == null) return undefined;\n let follower: Object;\n try {\n follower = await Object.fromJsonLd(followerJson);\n } catch {\n return undefined;\n }\n if (!isActor(follower)) return undefined;\n const lockKey: KvKey = [...this.prefixes.followers, \"lock\"];\n const listKey: KvKey = this.prefixes.followers;\n do {\n await this.kv.set(lockKey, followerId);\n let list = await this.kv.get<string[]>(listKey) ?? [];\n list = list.filter((id) => id !== followerId);\n await this.kv.set(listKey, list);\n } while (await this.kv.get(lockKey) !== followerId);\n await this.kv.delete(followerKey);\n await this.kv.delete(followRequestKey);\n return follower;\n }\n\n async hasFollower(followerId: URL): Promise<boolean> {\n return await this.kv.get<unknown>([\n ...this.prefixes.followers,\n followerId.href,\n ]) != null;\n }\n\n async *getFollowers(\n options: RepositoryGetFollowersOptions = {},\n ): AsyncIterable<Actor> {\n const { offset = 0, limit } = options;\n let followerIds = await this.kv.get<string[]>(this.prefixes.followers) ??\n [];\n followerIds = followerIds.slice(offset);\n if (limit != null) {\n followerIds = followerIds.slice(0, limit);\n }\n for (const id of followerIds) {\n const json = await this.kv.get([...this.prefixes.followers, id]);\n let actor: Object;\n try {\n actor = await Object.fromJsonLd(json);\n } catch (e) {\n if (e instanceof TypeError) continue;\n throw e;\n }\n if (isActor(actor)) yield actor;\n }\n }\n\n async countFollowers(): Promise<number> {\n const followerIds = await this.kv.get<string[]>(this.prefixes.followers) ??\n [];\n return followerIds.length;\n }\n\n async addSentFollow(id: Uuid, follow: Follow): Promise<void> {\n await this.kv.set(\n [...this.prefixes.follows, id],\n await follow.toJsonLd({ format: \"compact\" }),\n );\n }\n\n async removeSentFollow(id: Uuid): Promise<Follow | undefined> {\n const follow = await this.getSentFollow(id);\n if (follow == null) return undefined;\n await this.kv.delete([...this.prefixes.follows, id]);\n return follow;\n }\n\n async getSentFollow(id: Uuid): Promise<Follow | undefined> {\n const followJson = await this.kv.get([...this.prefixes.follows, id]);\n if (followJson == null) return undefined;\n try {\n return await Follow.fromJsonLd(followJson);\n } catch {\n return undefined;\n }\n }\n\n async addFollowee(followeeId: URL, follow: Follow): Promise<void> {\n await this.kv.set(\n [...this.prefixes.followees, followeeId.href],\n await follow.toJsonLd({ format: \"compact\" }),\n );\n }\n\n async removeFollowee(followeeId: URL): Promise<Follow | undefined> {\n const follow = await this.getFollowee(followeeId);\n if (follow == null) return undefined;\n await this.kv.delete([...this.prefixes.followees, followeeId.href]);\n return follow;\n }\n\n async getFollowee(followeeId: URL): Promise<Follow | undefined> {\n const json = await this.kv.get([\n ...this.prefixes.followees,\n followeeId.href,\n ]);\n if (json == null) return undefined;\n try {\n return await Follow.fromJsonLd(json);\n } catch {\n return undefined;\n }\n }\n\n async vote(messageId: Uuid, voterId: URL, option: string): Promise<void> {\n const key: KvKey = [...this.prefixes.polls, messageId, option];\n while (true) {\n const prev = await this.kv.get<string[]>(key);\n if (prev != null && prev.includes(voterId.href)) return;\n const next = prev == null ? [voterId.href] : [...prev, voterId.href];\n if (this.kv.cas == null) {\n this.kv.set(key, next);\n break;\n } else {\n const success = await this.kv.cas(key, prev, next);\n if (success) break;\n // If the CAS operation failed, we retry to ensure the vote is recorded.\n logger.trace(\n \"CAS operation failed, retrying vote for {messageId} by {voterId} for option {option}.\",\n {\n messageId,\n voterId: voterId.href,\n option,\n },\n );\n }\n }\n const optionsKey: KvKey = [...this.prefixes.polls, messageId];\n while (true) {\n const prevOptions = await this.kv.get<string[]>(optionsKey);\n if (prevOptions != null && prevOptions.includes(option)) return;\n const nextOptions = prevOptions == null\n ? [option]\n : [...prevOptions, option];\n if (this.kv.cas == null) {\n this.kv.set(optionsKey, nextOptions);\n break;\n } else {\n const success = await this.kv.cas(optionsKey, prevOptions, nextOptions);\n if (success) break;\n // If the CAS operation failed, we retry to ensure the option is recorded.\n logger.trace(\n \"CAS operation failed, retrying to add option {option} for message {messageId}.\",\n {\n option,\n messageId,\n },\n );\n }\n }\n }\n\n async countVoters(messageId: Uuid): Promise<number> {\n const options = await this.kv.get<string[]>([\n ...this.prefixes.polls,\n messageId,\n ]) ?? [];\n const result = new Set<string>();\n for (const option of options) {\n const voters = await this.kv.get<string[]>([\n ...this.prefixes.polls,\n messageId,\n option,\n ]);\n if (voters != null) {\n for (const voter of voters) result.add(voter);\n }\n }\n return result.size;\n }\n\n async countVotes(messageId: Uuid): Promise<Readonly<Record<string, number>>> {\n const options = await this.kv.get<string[]>([\n ...this.prefixes.polls,\n messageId,\n ]) ?? [];\n const result: Record<string, number> = {};\n for (const option of options) {\n const voters = await this.kv.get<string[]>([\n ...this.prefixes.polls,\n messageId,\n option,\n ]);\n result[option] = voters == null ? 0 : voters.length;\n }\n return result;\n }\n}\n\ninterface KeyPair {\n private: JsonWebKey;\n public: JsonWebKey;\n}\n\n/**\n * Extracts the timestamp from a UUIDv7.\n * @param uuid The UUIDv7 string to extract the timestamp from.\n * @return The timestamp in milliseconds since the Unix epoch.\n * @internal\n */\nfunction extractTimestamp(uuid: string): number {\n // UUIDv7 format: xxxxxxxx-xxxx-7xxx-xxxx-xxxxxxxxxxxx\n // The timestamp is in the first 6 bytes (48 bits) of the UUID.\n if (uuid.length !== 36 || uuid[14] !== \"7\") {\n throw new TypeError(\"Invalid UUIDv7 format.\");\n }\n const timestampHex = uuid.slice(0, 8) + uuid.slice(9, 13);\n return parseInt(timestampHex, 16);\n}\n\n/**\n * A repository for storing bot data in memory. This repository is not\n * persistent and is only suitable for testing or development.\n */\nexport class MemoryRepository implements Repository {\n keyPairs?: CryptoKeyPair[];\n messages: Map<Uuid, Create | Announce> = new Map();\n followers: Map<string, Actor> = new Map();\n followRequests: Record<string, string> = {};\n sentFollows: Record<string, Follow> = {};\n followees: Record<string, Follow> = {};\n polls: Record<Uuid, Record<string, Set<string>>> = {};\n\n setKeyPairs(keyPairs: CryptoKeyPair[]): Promise<void> {\n this.keyPairs = keyPairs;\n return Promise.resolve();\n }\n\n getKeyPairs(): Promise<CryptoKeyPair[] | undefined> {\n return Promise.resolve(this.keyPairs);\n }\n\n addMessage(id: Uuid, activity: Create | Announce): Promise<void> {\n this.messages.set(id, activity);\n return Promise.resolve();\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 existing = this.messages.get(id);\n if (existing == null) return false;\n const newActivity = await updater(existing);\n if (newActivity == null) return false;\n this.messages.set(id, newActivity);\n return true;\n }\n\n removeMessage(id: Uuid): Promise<Create | Announce | undefined> {\n const activity = this.messages.get(id);\n this.messages.delete(id);\n return Promise.resolve(activity);\n }\n\n async *getMessages(\n options: RepositoryGetMessagesOptions = {},\n ): AsyncIterable<Create | Announce> {\n const { order, until, since, limit } = options;\n let messages = [...this.messages.values()];\n if (since != null) {\n messages = messages.filter((message) =>\n message.published != null &&\n Temporal.Instant.compare(message.published, since) >= 0\n );\n }\n if (until != null) {\n messages = messages.filter((message) =>\n message.published != null &&\n Temporal.Instant.compare(message.published, until) <= 0\n );\n }\n if (order === \"oldest\") {\n messages.sort((a, b) =>\n (a.published?.epochMilliseconds ?? 0) -\n (b.published?.epochMilliseconds ?? 0)\n );\n } else {\n messages.sort((a, b) =>\n (b.published?.epochMilliseconds ?? 0) -\n (a.published?.epochMilliseconds ?? 0)\n );\n }\n if (limit != null) {\n messages.slice(0, limit);\n }\n for (const message of messages) yield message;\n }\n\n getMessage(id: Uuid): Promise<Create | Announce | undefined> {\n return Promise.resolve(this.messages.get(id));\n }\n\n countMessages(): Promise<number> {\n return Promise.resolve(this.messages.size);\n }\n\n addFollower(followId: URL, follower: Actor): Promise<void> {\n if (follower.id == null) {\n throw new TypeError(\"The follower ID is missing.\");\n }\n this.followers.set(follower.id.href, follower);\n this.followRequests[followId.href] = follower.id.href;\n return Promise.resolve();\n }\n\n removeFollower(followId: URL, followerId: URL): Promise<Actor | undefined> {\n const existing = this.followRequests[followId.href];\n if (existing == null || existing !== followerId.href) {\n return Promise.resolve(undefined);\n }\n delete this.followRequests[followId.href];\n const follower = this.followers.get(followerId.href);\n this.followers.delete(followerId.href);\n return Promise.resolve(follower);\n }\n\n hasFollower(followerId: URL): Promise<boolean> {\n return Promise.resolve(this.followers.has(followerId.href));\n }\n\n async *getFollowers(\n options: RepositoryGetFollowersOptions = {},\n ): AsyncIterable<Actor> {\n const { offset = 0, limit } = options;\n let followers = [...this.followers.values()];\n followers.sort((a, b) => b.id!.href.localeCompare(a.id!.href) ?? 0);\n if (offset > 0) {\n followers = followers.slice(offset);\n }\n if (limit != null) {\n followers = followers.slice(0, limit);\n }\n for (const follower of followers) {\n yield follower;\n }\n }\n\n countFollowers(): Promise<number> {\n return Promise.resolve(this.followers.size);\n }\n\n addSentFollow(id: Uuid, follow: Follow): Promise<void> {\n this.sentFollows[id] = follow;\n return Promise.resolve();\n }\n\n removeSentFollow(id: Uuid): Promise<Follow | undefined> {\n const follow = this.sentFollows[id];\n delete this.sentFollows[id];\n return Promise.resolve(follow);\n }\n\n getSentFollow(id: Uuid): Promise<Follow | undefined> {\n return Promise.resolve(this.sentFollows[id]);\n }\n\n addFollowee(followeeId: URL, follow: Follow): Promise<void> {\n this.followees[followeeId.href] = follow;\n return Promise.resolve();\n }\n\n removeFollowee(followeeId: URL): Promise<Follow | undefined> {\n const follow = this.followees[followeeId.href];\n delete this.followees[followeeId.href];\n return Promise.resolve(follow);\n }\n\n getFollowee(followeeId: URL): Promise<Follow | undefined> {\n return Promise.resolve(this.followees[followeeId.href]);\n }\n\n vote(messageId: Uuid, voterId: URL, option: string): Promise<void> {\n const poll = this.polls[messageId] ??= {};\n const voters = poll[option] ??= new Set();\n voters.add(voterId.href);\n return Promise.resolve();\n }\n\n countVoters(messageId: Uuid): Promise<number> {\n const poll = this.polls[messageId];\n if (poll == null) return Promise.resolve(0);\n let voters = new Set<string>();\n for (const votersSet of globalThis.Object.values(poll)) {\n voters = voters.union(votersSet);\n }\n return Promise.resolve(voters.size);\n }\n\n countVotes(messageId: Uuid): Promise<Readonly<Record<string, number>>> {\n const poll = this.polls[messageId];\n if (poll == null) return Promise.resolve({});\n const counts: Record<string, number> = {};\n for (const [option, voters] of globalThis.Object.entries(poll)) {\n counts[option] = voters.size;\n }\n return Promise.resolve(counts);\n }\n}\n\n/**\n * A repository decorator that adds an in-memory cache layer on top of another\n * repository. This is useful for improving performance by reducing the number\n * of accesses to the underlying persistent storage, but it increases memory\n * usage. The cache is not persistent and will be lost when the process exits.\n *\n * Note: List operations like `getMessages` and `getFollowers`, and count\n * operations like `countMessages` and `countFollowers` are not cached and\n * always delegate to the underlying repository.\n * @since 0.3.0\n */\nexport class MemoryCachedRepository implements Repository {\n private underlying: Repository;\n private cache: MemoryRepository;\n\n /**\n * Creates a new memory-cached repository.\n * @param underlying The underlying repository to cache.\n * @param cache An optional `MemoryRepository` instance to use as the cache.\n * If not provided, a new one will be created internally.\n */\n constructor(underlying: Repository, cache?: MemoryRepository) {\n this.underlying = underlying;\n this.cache = cache ?? new MemoryRepository();\n }\n\n async setKeyPairs(keyPairs: CryptoKeyPair[]): Promise<void> {\n await this.underlying.setKeyPairs(keyPairs);\n await this.cache.setKeyPairs(keyPairs);\n }\n\n async getKeyPairs(): Promise<CryptoKeyPair[] | undefined> {\n let keyPairs = await this.cache.getKeyPairs();\n if (keyPairs === undefined) {\n keyPairs = await this.underlying.getKeyPairs();\n if (keyPairs !== undefined) await this.cache.setKeyPairs(keyPairs);\n }\n return keyPairs;\n }\n\n async addMessage(id: Uuid, activity: Create | Announce): Promise<void> {\n await this.underlying.addMessage(id, activity);\n await this.cache.addMessage(id, activity);\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 // Apply update to underlying first\n const updated = await this.underlying.updateMessage(id, updater);\n if (updated) {\n // If successful, fetch the updated message and update the cache\n const updatedMessage = await this.underlying.getMessage(id);\n if (updatedMessage) {\n await this.cache.addMessage(id, updatedMessage); // Use addMessage which acts like set\n } else {\n // Should not happen if updateMessage returned true, but handle defensively\n await this.cache.removeMessage(id);\n }\n }\n return updated;\n }\n\n async removeMessage(id: Uuid): Promise<Create | Announce | undefined> {\n const removedActivity = await this.underlying.removeMessage(id);\n if (removedActivity !== undefined) {\n await this.cache.removeMessage(id);\n }\n return removedActivity;\n }\n\n // getMessages is not cached due to complexity with options\n getMessages(\n options?: RepositoryGetMessagesOptions,\n ): AsyncIterable<Create | Announce> {\n return this.underlying.getMessages(options);\n }\n\n async getMessage(id: Uuid): Promise<Create | Announce | undefined> {\n let message = await this.cache.getMessage(id);\n if (message === undefined) {\n message = await this.underlying.getMessage(id);\n if (message !== undefined) {\n await this.cache.addMessage(id, message); // Use addMessage which acts like set\n }\n }\n return message;\n }\n\n // countMessages is not cached\n countMessages(): Promise<number> {\n return this.underlying.countMessages();\n }\n\n async addFollower(followId: URL, follower: Actor): Promise<void> {\n await this.underlying.addFollower(followId, follower);\n await this.cache.addFollower(followId, follower);\n }\n\n async removeFollower(\n followId: URL,\n followerId: URL,\n ): Promise<Actor | undefined> {\n const removedFollower = await this.underlying.removeFollower(\n followId,\n followerId,\n );\n if (removedFollower !== undefined) {\n await this.cache.removeFollower(followId, followerId);\n }\n return removedFollower;\n }\n\n async hasFollower(followerId: URL): Promise<boolean> {\n // Check cache first for potentially faster response\n if (await this.cache.hasFollower(followerId)) {\n return true;\n }\n // If not in cache, check underlying and update cache if found\n const exists = await this.underlying.hasFollower(followerId);\n // Note: We don't automatically add to cache here, as we don't have the Actor object\n // It will be cached if addFollower is called or if getFollowers iterates over it (though getFollowers isn't cached)\n return exists;\n }\n\n // getFollowers is not cached due to complexity with options\n getFollowers(options?: RepositoryGetFollowersOptions): AsyncIterable<Actor> {\n // We could potentially cache followers as they are iterated,\n // but for simplicity, delegate directly for now.\n return this.underlying.getFollowers(options);\n }\n\n // countFollowers is not cached\n countFollowers(): Promise<number> {\n return this.underlying.countFollowers();\n }\n\n async addSentFollow(id: Uuid, follow: Follow): Promise<void> {\n await this.underlying.addSentFollow(id, follow);\n await this.cache.addSentFollow(id, follow);\n }\n\n async removeSentFollow(id: Uuid): Promise<Follow | undefined> {\n const removedFollow = await this.underlying.removeSentFollow(id);\n if (removedFollow !== undefined) {\n await this.cache.removeSentFollow(id);\n }\n return removedFollow;\n }\n\n async getSentFollow(id: Uuid): Promise<Follow | undefined> {\n let follow = await this.cache.getSentFollow(id);\n if (follow === undefined) {\n follow = await this.underlying.getSentFollow(id);\n if (follow !== undefined) {\n await this.cache.addSentFollow(id, follow);\n }\n }\n return follow;\n }\n\n async addFollowee(followeeId: URL, follow: Follow): Promise<void> {\n await this.underlying.addFollowee(followeeId, follow);\n await this.cache.addFollowee(followeeId, follow);\n }\n\n async removeFollowee(followeeId: URL): Promise<Follow | undefined> {\n const removedFollow = await this.underlying.removeFollowee(followeeId);\n if (removedFollow !== undefined) {\n await this.cache.removeFollowee(followeeId);\n }\n return removedFollow;\n }\n\n async getFollowee(followeeId: URL): Promise<Follow | undefined> {\n let follow = await this.cache.getFollowee(followeeId);\n if (follow === undefined) {\n follow = await this.underlying.getFollowee(followeeId);\n if (follow !== undefined) {\n await this.cache.addFollowee(followeeId, follow);\n }\n }\n return follow;\n }\n\n async vote(messageId: Uuid, voterId: URL, option: string): Promise<void> {\n await this.cache.vote(messageId, voterId, option);\n await this.underlying.vote(messageId, voterId, option);\n }\n\n async countVoters(messageId: Uuid): Promise<number> {\n const voters = await this.cache.countVoters(messageId);\n if (voters > 0) return voters;\n return this.underlying.countVoters(messageId);\n }\n\n async countVotes(messageId: Uuid): Promise<Readonly<Record<string, number>>> {\n const votes = await this.cache.countVotes(messageId);\n if (globalThis.Object.keys(votes).length > 0) return votes;\n return await this.underlying.countVotes(messageId);\n }\n}\n"],"mappings":";;;;;;;;;AA8BA,MAAM,SAAS,UAAU,CAAC,UAAU,YAAa,EAAC;;;;AA+SlD,IAAa,eAAb,MAAgD;CAC9C,AAAS;CACT,AAAS;;;;;;CAOT,YAAYA,IAAaC,UAAsC;AAC7D,MAAI,GAAG,OAAO,KACZ,QAAO,KACL,0GAEA,EAAE,GAAI,EACP;AAEH,OAAK,KAAK;AACV,OAAK,WAAW;GACd,UAAU,CAAC,WAAW,UAAW;GACjC,UAAU,CAAC,WAAW,UAAW;GACjC,WAAW,CAAC,WAAW,WAAY;GACnC,gBAAgB,CAAC,WAAW,gBAAiB;GAC7C,WAAW,CAAC,WAAW,WAAY;GACnC,SAAS,CAAC,WAAW,SAAU;GAC/B,OAAO,CAAC,WAAW,OAAQ;GAC3B,GAAG,YAAY,CAAE;EAClB;CACF;CAED,MAAM,YAAYC,UAA0C;EAC1D,MAAMC,QAAmB,CAAE;AAC3B,OAAK,MAAM,WAAW,UAAU;GAC9B,MAAMC,OAAgB;IACpB,SAAS,MAAM,UAAU,QAAQ,WAAW;IAC5C,QAAQ,MAAM,UAAU,QAAQ,UAAU;GAC3C;AACD,SAAM,KAAK,KAAK;EACjB;AACD,QAAM,KAAK,GAAG,IAAI,KAAK,SAAS,UAAU,MAAM;CACjD;CAED,MAAM,cAAoD;EACxD,MAAM,WAAW,MAAM,KAAK,GAAG,IAAe,KAAK,SAAS,SAAS;AACrE,MAAI,YAAY,KAAM;EACtB,MAAM,WAAW,SAAS,IAAI,OAAO,UAAU;GAC7C,YAAY,MAAM,UAAU,KAAK,SAAS,UAAU;GACpD,WAAW,MAAM,UAAU,KAAK,QAAQ,SAAS;EAClD,GAAE;AACH,SAAO,MAAM,QAAQ,IAAI,SAAS;CACnC;CAED,MAAM,WAAWC,IAAUC,UAA4C;EACrE,MAAMC,aAAoB,CAAC,GAAG,KAAK,SAAS,UAAU,EAAG;AACzD,QAAM,KAAK,GAAG,IACZ,YACA,MAAM,SAAS,SAAS,EAAE,QAAQ,UAAW,EAAC,CAC/C;EACD,MAAMC,UAAiB,CAAC,GAAG,KAAK,SAAS,UAAU,MAAO;EAC1D,MAAMC,UAAiB,KAAK,SAAS;AACrC,KAAG;AACD,SAAM,KAAK,GAAG,IAAI,SAAS,GAAG;GAC9B,MAAM,MAAM,IAAI,IAAI,MAAM,KAAK,GAAG,IAAc,QAAQ,IAAI,CAAE;AAC9D,OAAI,IAAI,GAAG;GACX,MAAM,OAAO,CAAC,GAAG,GAAI;AACrB,QAAK,KAAK,CAAC,GAAG,MAAM,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,EAAE;AAC/C,SAAM,KAAK,GAAG,IAAI,SAAS,KAAK;EACjC,SAAQ,MAAM,KAAK,GAAG,IAAI,QAAQ,KAAK;CACzC;CAED,MAAM,cACJJ,IACAK,SAGkB;EAClB,MAAMC,QAAe,CAAC,GAAG,KAAK,SAAS,UAAU,EAAG;EACpD,MAAM,aAAa,MAAM,KAAK,GAAG,IAAI,MAAM;AAC3C,MAAI,cAAc,KAAM,QAAO;EAC/B,MAAM,WAAW,MAAM,SAAS,WAAW,WAAW;AACtD,QAAM,oBAAoBC,YAAU,oBAAoBC,YACtD,QAAO;EAET,MAAM,cAAc,MAAM,QAAQ,SAAS;AAC3C,MAAI,eAAe,KAAM,QAAO;AAChC,QAAM,KAAK,GAAG,IACZ,OACA,MAAM,YAAY,SAAS,EAAE,QAAQ,UAAW,EAAC,CAClD;AACD,SAAO;CACR;CAED,MAAM,cAAcR,IAAkD;EACpE,MAAMI,UAAiB,KAAK,SAAS;EACrC,MAAMD,UAAiB,CAAC,GAAG,SAAS,MAAO;EAC3C,MAAM,UAAU,EAAE,GAAG;AACrB,KAAG;AACD,SAAM,KAAK,GAAG,IAAI,SAAS,OAAO;GAClC,MAAM,MAAM,IAAI,IAAI,MAAM,KAAK,GAAG,IAAc,QAAQ,IAAI,CAAE;AAC9D,OAAI,OAAO,GAAG;GACd,MAAM,OAAO,CAAC,GAAG,GAAI;AACrB,QAAK,KAAK,CAAC,GAAG,MAAM,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,EAAE;AAC/C,SAAM,KAAK,GAAG,IAAI,SAAS,KAAK;EACjC,SAAQ,MAAM,KAAK,GAAG,IAAI,QAAQ,KAAK;EACxC,MAAMD,aAAoB,CAAC,GAAG,SAAS,EAAG;EAC1C,MAAM,eAAe,MAAM,KAAK,GAAG,IAAI,WAAW;AAClD,MAAI,gBAAgB,KAAM;AAC1B,QAAM,KAAK,GAAG,OAAO,WAAW;EAChC,MAAM,WAAW,MAAM,SAAS,WAAW,aAAa;AACxD,MAAI,oBAAoBK,YAAU,oBAAoBC,WACpD,QAAO;AAET;CACD;CAED,OAAO,YACLC,UAAwC,CAAE,GACR;EAClC,MAAM,EAAE,OAAO,OAAO,OAAO,OAAO,GAAG;EACvC,MAAM,UAAU,SAAS,OAAO,OAAO,MAAM;EAC7C,MAAM,UAAU,SAAS,OAAO,OAAO,MAAM;EAC7C,IAAI,aAAa,MAAM,KAAK,GAAG,IAAc,KAAK,SAAS,SAAS,IAAI,CAAE;AAC1E,MAAI,WAAW,MAAM;GACnB,MAAM,SAAS,WAAW,UAAU,CAAC,OACnC,iBAAiB,GAAG,IAAI,QACzB;AACD,gBAAa,WAAW,MAAM,OAAO;EACtC;AACD,MAAI,WAAW,MAAM;GACnB,MAAM,SAAS,WAAW,cAAc,CAAC,OACvC,iBAAiB,GAAG,IAAI,QACzB;AACD,gBAAa,WAAW,MAAM,GAAG,SAAS,EAAE;EAC7C;AACD,MAAI,SAAS,QAAQ,UAAU,SAC7B,cAAa,WAAW,YAAY;AAEtC,MAAI,SAAS,KACX,cAAa,WAAW,MAAM,GAAG,MAAM;AAEzC,OAAK,MAAM,MAAM,YAAY;GAC3B,MAAM,cAAc,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,KAAK,SAAS,UAAU,EAAG,EAAC;AACtE,OAAI,eAAe,KAAM;AACzB,OAAI;IACF,MAAM,WAAW,MAAM,SAAS,WAAW,YAAY;AACvD,QAAI,oBAAoBF,YAAU,oBAAoBC,WACpD,OAAM;GAET,QAAO;AACN;GACD;EACF;CACF;CAED,MAAM,WAAWR,IAAkD;EACjE,MAAM,OAAO,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,KAAK,SAAS,UAAU,EAAG,EAAC;AAC/D,MAAI,QAAQ,KAAM;EAClB,IAAIU;AACJ,MAAI;AACF,cAAW,MAAM,SAAS,WAAW,KAAK;EAC3C,SAAQ,GAAG;AACV,OAAI,aAAa,UAAW;AAC5B,SAAM;EACP;AACD,MAAI,oBAAoBH,YAAU,oBAAoBC,WACpD,QAAO;AAET;CACD;CAED,MAAM,gBAAiC;EACrC,MAAM,aAAa,MAAM,KAAK,GAAG,IAAc,KAAK,SAAS,SAAS,IACpE,CAAE;AACJ,SAAO,WAAW;CACnB;CAED,MAAM,YAAYG,iBAAsBC,UAAgC;AACtE,MAAI,SAAS,MAAM,KACjB,OAAM,IAAI,UAAU;EAEtB,MAAMC,cAAqB,CAAC,GAAG,KAAK,SAAS,WAAW,SAAS,GAAG,IAAK;AACzE,QAAM,KAAK,GAAG,IACZ,aACA,MAAM,SAAS,SAAS,EAAE,QAAQ,UAAW,EAAC,CAC/C;EACD,MAAMV,UAAiB,CAAC,GAAG,KAAK,SAAS,WAAW,MAAO;EAC3D,MAAMC,UAAiB,KAAK,SAAS;AACrC,KAAG;AACD,SAAM,KAAK,GAAG,IAAI,SAAS,SAAS,GAAG,KAAK;GAC5C,MAAM,OAAO,MAAM,KAAK,GAAG,IAAc,QAAQ,IAAI,CAAE;AACvD,QAAK,KAAK,SAAS,SAAS,GAAG,KAAK,CAAE,MAAK,KAAK,SAAS,GAAG,KAAK;AACjE,SAAM,KAAK,GAAG,IAAI,SAAS,KAAK;EACjC,SAAQ,MAAM,KAAK,GAAG,IAAI,QAAQ,KAAK,SAAS,GAAG;EACpD,MAAMU,mBAA0B,CAC9B,GAAG,KAAK,SAAS,gBACjB,gBAAgB,IACjB;AACD,QAAM,KAAK,GAAG,IAAI,kBAAkB,SAAS,GAAG,KAAK;CACtD;CAED,MAAM,eACJH,iBACAI,SAC4B;EAC5B,MAAMD,mBAA0B,CAC9B,GAAG,KAAK,SAAS,gBACjB,gBAAgB,IACjB;EACD,MAAM,aAAa,MAAM,KAAK,GAAG,IAAY,iBAAiB;AAC9D,MAAI,cAAc,KAAM;EACxB,MAAMD,cAAqB,CAAC,GAAG,KAAK,SAAS,WAAW,UAAW;AACnE,MAAI,eAAe,QAAQ,KAAM;EACjC,MAAM,eAAe,MAAM,KAAK,GAAG,IAAI,YAAY;AACnD,MAAI,gBAAgB,KAAM;EAC1B,IAAIG;AACJ,MAAI;AACF,cAAW,MAAM,SAAO,WAAW,aAAa;EACjD,QAAO;AACN;EACD;AACD,OAAK,QAAQ,SAAS,CAAE;EACxB,MAAMb,UAAiB,CAAC,GAAG,KAAK,SAAS,WAAW,MAAO;EAC3D,MAAMC,UAAiB,KAAK,SAAS;AACrC,KAAG;AACD,SAAM,KAAK,GAAG,IAAI,SAAS,WAAW;GACtC,IAAI,OAAO,MAAM,KAAK,GAAG,IAAc,QAAQ,IAAI,CAAE;AACrD,UAAO,KAAK,OAAO,CAAC,OAAO,OAAO,WAAW;AAC7C,SAAM,KAAK,GAAG,IAAI,SAAS,KAAK;EACjC,SAAQ,MAAM,KAAK,GAAG,IAAI,QAAQ,KAAK;AACxC,QAAM,KAAK,GAAG,OAAO,YAAY;AACjC,QAAM,KAAK,GAAG,OAAO,iBAAiB;AACtC,SAAO;CACR;CAED,MAAM,YAAYa,YAAmC;AACnD,SAAO,MAAM,KAAK,GAAG,IAAa,CAChC,GAAG,KAAK,SAAS,WACjB,WAAW,IACZ,EAAC,IAAI;CACP;CAED,OAAO,aACLC,UAAyC,CAAE,GACrB;EACtB,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG;EAC9B,IAAI,cAAc,MAAM,KAAK,GAAG,IAAc,KAAK,SAAS,UAAU,IACpE,CAAE;AACJ,gBAAc,YAAY,MAAM,OAAO;AACvC,MAAI,SAAS,KACX,eAAc,YAAY,MAAM,GAAG,MAAM;AAE3C,OAAK,MAAM,MAAM,aAAa;GAC5B,MAAM,OAAO,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,KAAK,SAAS,WAAW,EAAG,EAAC;GAChE,IAAIC;AACJ,OAAI;AACF,YAAQ,MAAM,SAAO,WAAW,KAAK;GACtC,SAAQ,GAAG;AACV,QAAI,aAAa,UAAW;AAC5B,UAAM;GACP;AACD,OAAI,QAAQ,MAAM,CAAE,OAAM;EAC3B;CACF;CAED,MAAM,iBAAkC;EACtC,MAAM,cAAc,MAAM,KAAK,GAAG,IAAc,KAAK,SAAS,UAAU,IACtE,CAAE;AACJ,SAAO,YAAY;CACpB;CAED,MAAM,cAAcnB,IAAUoB,QAA+B;AAC3D,QAAM,KAAK,GAAG,IACZ,CAAC,GAAG,KAAK,SAAS,SAAS,EAAG,GAC9B,MAAM,OAAO,SAAS,EAAE,QAAQ,UAAW,EAAC,CAC7C;CACF;CAED,MAAM,iBAAiBpB,IAAuC;EAC5D,MAAM,SAAS,MAAM,KAAK,cAAc,GAAG;AAC3C,MAAI,UAAU,KAAM;AACpB,QAAM,KAAK,GAAG,OAAO,CAAC,GAAG,KAAK,SAAS,SAAS,EAAG,EAAC;AACpD,SAAO;CACR;CAED,MAAM,cAAcA,IAAuC;EACzD,MAAM,aAAa,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,KAAK,SAAS,SAAS,EAAG,EAAC;AACpE,MAAI,cAAc,KAAM;AACxB,MAAI;AACF,UAAO,MAAM,OAAO,WAAW,WAAW;EAC3C,QAAO;AACN;EACD;CACF;CAED,MAAM,YAAYqB,YAAiBD,QAA+B;AAChE,QAAM,KAAK,GAAG,IACZ,CAAC,GAAG,KAAK,SAAS,WAAW,WAAW,IAAK,GAC7C,MAAM,OAAO,SAAS,EAAE,QAAQ,UAAW,EAAC,CAC7C;CACF;CAED,MAAM,eAAeC,YAA8C;EACjE,MAAM,SAAS,MAAM,KAAK,YAAY,WAAW;AACjD,MAAI,UAAU,KAAM;AACpB,QAAM,KAAK,GAAG,OAAO,CAAC,GAAG,KAAK,SAAS,WAAW,WAAW,IAAK,EAAC;AACnE,SAAO;CACR;CAED,MAAM,YAAYA,YAA8C;EAC9D,MAAM,OAAO,MAAM,KAAK,GAAG,IAAI,CAC7B,GAAG,KAAK,SAAS,WACjB,WAAW,IACZ,EAAC;AACF,MAAI,QAAQ,KAAM;AAClB,MAAI;AACF,UAAO,MAAM,OAAO,WAAW,KAAK;EACrC,QAAO;AACN;EACD;CACF;CAED,MAAM,KAAKC,WAAiBC,SAAcC,QAA+B;EACvE,MAAMC,MAAa;GAAC,GAAG,KAAK,SAAS;GAAO;GAAW;EAAO;AAC9D,SAAO,MAAM;GACX,MAAM,OAAO,MAAM,KAAK,GAAG,IAAc,IAAI;AAC7C,OAAI,QAAQ,QAAQ,KAAK,SAAS,QAAQ,KAAK,CAAE;GACjD,MAAM,OAAO,QAAQ,OAAO,CAAC,QAAQ,IAAK,IAAG,CAAC,GAAG,MAAM,QAAQ,IAAK;AACpE,OAAI,KAAK,GAAG,OAAO,MAAM;AACvB,SAAK,GAAG,IAAI,KAAK,KAAK;AACtB;GACD,OAAM;IACL,MAAM,UAAU,MAAM,KAAK,GAAG,IAAI,KAAK,MAAM,KAAK;AAClD,QAAI,QAAS;AAEb,WAAO,MACL,yFACA;KACE;KACA,SAAS,QAAQ;KACjB;IACD,EACF;GACF;EACF;EACD,MAAMC,aAAoB,CAAC,GAAG,KAAK,SAAS,OAAO,SAAU;AAC7D,SAAO,MAAM;GACX,MAAM,cAAc,MAAM,KAAK,GAAG,IAAc,WAAW;AAC3D,OAAI,eAAe,QAAQ,YAAY,SAAS,OAAO,CAAE;GACzD,MAAM,cAAc,eAAe,OAC/B,CAAC,MAAO,IACR,CAAC,GAAG,aAAa,MAAO;AAC5B,OAAI,KAAK,GAAG,OAAO,MAAM;AACvB,SAAK,GAAG,IAAI,YAAY,YAAY;AACpC;GACD,OAAM;IACL,MAAM,UAAU,MAAM,KAAK,GAAG,IAAI,YAAY,aAAa,YAAY;AACvE,QAAI,QAAS;AAEb,WAAO,MACL,kFACA;KACE;KACA;IACD,EACF;GACF;EACF;CACF;CAED,MAAM,YAAYJ,WAAkC;EAClD,MAAM,UAAU,MAAM,KAAK,GAAG,IAAc,CAC1C,GAAG,KAAK,SAAS,OACjB,SACD,EAAC,IAAI,CAAE;EACR,MAAM,yBAAS,IAAI;AACnB,OAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,SAAS,MAAM,KAAK,GAAG,IAAc;IACzC,GAAG,KAAK,SAAS;IACjB;IACA;GACD,EAAC;AACF,OAAI,UAAU,KACZ,MAAK,MAAM,SAAS,OAAQ,QAAO,IAAI,MAAM;EAEhD;AACD,SAAO,OAAO;CACf;CAED,MAAM,WAAWA,WAA4D;EAC3E,MAAM,UAAU,MAAM,KAAK,GAAG,IAAc,CAC1C,GAAG,KAAK,SAAS,OACjB,SACD,EAAC,IAAI,CAAE;EACR,MAAMK,SAAiC,CAAE;AACzC,OAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,SAAS,MAAM,KAAK,GAAG,IAAc;IACzC,GAAG,KAAK,SAAS;IACjB;IACA;GACD,EAAC;AACF,UAAO,UAAU,UAAU,OAAO,IAAI,OAAO;EAC9C;AACD,SAAO;CACR;AACF;;;;;;;AAaD,SAAS,iBAAiBC,MAAsB;AAG9C,KAAI,KAAK,WAAW,MAAM,KAAK,QAAQ,IACrC,OAAM,IAAI,UAAU;CAEtB,MAAM,eAAe,KAAK,MAAM,GAAG,EAAE,GAAG,KAAK,MAAM,GAAG,GAAG;AACzD,QAAO,SAAS,cAAc,GAAG;AAClC;;;;;AAMD,IAAa,mBAAb,MAAoD;CAClD;CACA,2BAAyC,IAAI;CAC7C,4BAAgC,IAAI;CACpC,iBAAyC,CAAE;CAC3C,cAAsC,CAAE;CACxC,YAAoC,CAAE;CACtC,QAAmD,CAAE;CAErD,YAAY/B,UAA0C;AACpD,OAAK,WAAW;AAChB,SAAO,QAAQ,SAAS;CACzB;CAED,cAAoD;AAClD,SAAO,QAAQ,QAAQ,KAAK,SAAS;CACtC;CAED,WAAWG,IAAUC,UAA4C;AAC/D,OAAK,SAAS,IAAI,IAAI,SAAS;AAC/B,SAAO,QAAQ,SAAS;CACzB;CAED,MAAM,cACJD,IACAK,SAGkB;EAClB,MAAM,WAAW,KAAK,SAAS,IAAI,GAAG;AACtC,MAAI,YAAY,KAAM,QAAO;EAC7B,MAAM,cAAc,MAAM,QAAQ,SAAS;AAC3C,MAAI,eAAe,KAAM,QAAO;AAChC,OAAK,SAAS,IAAI,IAAI,YAAY;AAClC,SAAO;CACR;CAED,cAAcL,IAAkD;EAC9D,MAAM,WAAW,KAAK,SAAS,IAAI,GAAG;AACtC,OAAK,SAAS,OAAO,GAAG;AACxB,SAAO,QAAQ,QAAQ,SAAS;CACjC;CAED,OAAO,YACLS,UAAwC,CAAE,GACR;EAClC,MAAM,EAAE,OAAO,OAAO,OAAO,OAAO,GAAG;EACvC,IAAI,WAAW,CAAC,GAAG,KAAK,SAAS,QAAQ,AAAC;AAC1C,MAAI,SAAS,KACX,YAAW,SAAS,OAAO,CAAC,YAC1B,QAAQ,aAAa,QACrB,SAAS,QAAQ,QAAQ,QAAQ,WAAW,MAAM,IAAI,EACvD;AAEH,MAAI,SAAS,KACX,YAAW,SAAS,OAAO,CAAC,YAC1B,QAAQ,aAAa,QACrB,SAAS,QAAQ,QAAQ,QAAQ,WAAW,MAAM,IAAI,EACvD;AAEH,MAAI,UAAU,SACZ,UAAS,KAAK,CAAC,GAAG,OACf,EAAE,WAAW,qBAAqB,MAClC,EAAE,WAAW,qBAAqB,GACpC;MAED,UAAS,KAAK,CAAC,GAAG,OACf,EAAE,WAAW,qBAAqB,MAClC,EAAE,WAAW,qBAAqB,GACpC;AAEH,MAAI,SAAS,KACX,UAAS,MAAM,GAAG,MAAM;AAE1B,OAAK,MAAM,WAAW,SAAU,OAAM;CACvC;CAED,WAAWT,IAAkD;AAC3D,SAAO,QAAQ,QAAQ,KAAK,SAAS,IAAI,GAAG,CAAC;CAC9C;CAED,gBAAiC;AAC/B,SAAO,QAAQ,QAAQ,KAAK,SAAS,KAAK;CAC3C;CAED,YAAY6B,UAAejB,UAAgC;AACzD,MAAI,SAAS,MAAM,KACjB,OAAM,IAAI,UAAU;AAEtB,OAAK,UAAU,IAAI,SAAS,GAAG,MAAM,SAAS;AAC9C,OAAK,eAAe,SAAS,QAAQ,SAAS,GAAG;AACjD,SAAO,QAAQ,SAAS;CACzB;CAED,eAAeiB,UAAeZ,YAA6C;EACzE,MAAM,WAAW,KAAK,eAAe,SAAS;AAC9C,MAAI,YAAY,QAAQ,aAAa,WAAW,KAC9C,QAAO,QAAQ,eAAkB;AAEnC,SAAO,KAAK,eAAe,SAAS;EACpC,MAAM,WAAW,KAAK,UAAU,IAAI,WAAW,KAAK;AACpD,OAAK,UAAU,OAAO,WAAW,KAAK;AACtC,SAAO,QAAQ,QAAQ,SAAS;CACjC;CAED,YAAYA,YAAmC;AAC7C,SAAO,QAAQ,QAAQ,KAAK,UAAU,IAAI,WAAW,KAAK,CAAC;CAC5D;CAED,OAAO,aACLC,UAAyC,CAAE,GACrB;EACtB,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG;EAC9B,IAAI,YAAY,CAAC,GAAG,KAAK,UAAU,QAAQ,AAAC;AAC5C,YAAU,KAAK,CAAC,GAAG,MAAM,EAAE,GAAI,KAAK,cAAc,EAAE,GAAI,KAAK,IAAI,EAAE;AACnE,MAAI,SAAS,EACX,aAAY,UAAU,MAAM,OAAO;AAErC,MAAI,SAAS,KACX,aAAY,UAAU,MAAM,GAAG,MAAM;AAEvC,OAAK,MAAM,YAAY,UACrB,OAAM;CAET;CAED,iBAAkC;AAChC,SAAO,QAAQ,QAAQ,KAAK,UAAU,KAAK;CAC5C;CAED,cAAclB,IAAUoB,QAA+B;AACrD,OAAK,YAAY,MAAM;AACvB,SAAO,QAAQ,SAAS;CACzB;CAED,iBAAiBpB,IAAuC;EACtD,MAAM,SAAS,KAAK,YAAY;AAChC,SAAO,KAAK,YAAY;AACxB,SAAO,QAAQ,QAAQ,OAAO;CAC/B;CAED,cAAcA,IAAuC;AACnD,SAAO,QAAQ,QAAQ,KAAK,YAAY,IAAI;CAC7C;CAED,YAAYqB,YAAiBD,QAA+B;AAC1D,OAAK,UAAU,WAAW,QAAQ;AAClC,SAAO,QAAQ,SAAS;CACzB;CAED,eAAeC,YAA8C;EAC3D,MAAM,SAAS,KAAK,UAAU,WAAW;AACzC,SAAO,KAAK,UAAU,WAAW;AACjC,SAAO,QAAQ,QAAQ,OAAO;CAC/B;CAED,YAAYA,YAA8C;AACxD,SAAO,QAAQ,QAAQ,KAAK,UAAU,WAAW,MAAM;CACxD;CAED,KAAKC,WAAiBC,SAAcC,QAA+B;EACjE,MAAM,OAAO,KAAK,MAAM,eAAe,CAAE;EACzC,MAAM,SAAS,KAAK,4BAAY,IAAI;AACpC,SAAO,IAAI,QAAQ,KAAK;AACxB,SAAO,QAAQ,SAAS;CACzB;CAED,YAAYF,WAAkC;EAC5C,MAAM,OAAO,KAAK,MAAM;AACxB,MAAI,QAAQ,KAAM,QAAO,QAAQ,QAAQ,EAAE;EAC3C,IAAI,yBAAS,IAAI;AACjB,OAAK,MAAM,aAAa,WAAW,OAAO,OAAO,KAAK,CACpD,UAAS,OAAO,MAAM,UAAU;AAElC,SAAO,QAAQ,QAAQ,OAAO,KAAK;CACpC;CAED,WAAWA,WAA4D;EACrE,MAAM,OAAO,KAAK,MAAM;AACxB,MAAI,QAAQ,KAAM,QAAO,QAAQ,QAAQ,CAAE,EAAC;EAC5C,MAAMQ,SAAiC,CAAE;AACzC,OAAK,MAAM,CAAC,QAAQ,OAAO,IAAI,WAAW,OAAO,QAAQ,KAAK,CAC5D,QAAO,UAAU,OAAO;AAE1B,SAAO,QAAQ,QAAQ,OAAO;CAC/B;AACF;;;;;;;;;;;;AAaD,IAAa,yBAAb,MAA0D;CACxD,AAAQ;CACR,AAAQ;;;;;;;CAQR,YAAYC,YAAwBC,OAA0B;AAC5D,OAAK,aAAa;AAClB,OAAK,QAAQ,SAAS,IAAI;CAC3B;CAED,MAAM,YAAYnC,UAA0C;AAC1D,QAAM,KAAK,WAAW,YAAY,SAAS;AAC3C,QAAM,KAAK,MAAM,YAAY,SAAS;CACvC;CAED,MAAM,cAAoD;EACxD,IAAI,WAAW,MAAM,KAAK,MAAM,aAAa;AAC7C,MAAI,qBAAwB;AAC1B,cAAW,MAAM,KAAK,WAAW,aAAa;AAC9C,OAAI,oBAAwB,OAAM,KAAK,MAAM,YAAY,SAAS;EACnE;AACD,SAAO;CACR;CAED,MAAM,WAAWG,IAAUC,UAA4C;AACrE,QAAM,KAAK,WAAW,WAAW,IAAI,SAAS;AAC9C,QAAM,KAAK,MAAM,WAAW,IAAI,SAAS;CAC1C;CAED,MAAM,cACJD,IACAK,SAGkB;EAElB,MAAM,UAAU,MAAM,KAAK,WAAW,cAAc,IAAI,QAAQ;AAChE,MAAI,SAAS;GAEX,MAAM,iBAAiB,MAAM,KAAK,WAAW,WAAW,GAAG;AAC3D,OAAI,eACF,OAAM,KAAK,MAAM,WAAW,IAAI,eAAe;OAG/C,OAAM,KAAK,MAAM,cAAc,GAAG;EAErC;AACD,SAAO;CACR;CAED,MAAM,cAAcL,IAAkD;EACpE,MAAM,kBAAkB,MAAM,KAAK,WAAW,cAAc,GAAG;AAC/D,MAAI,2BACF,OAAM,KAAK,MAAM,cAAc,GAAG;AAEpC,SAAO;CACR;CAGD,YACEiC,SACkC;AAClC,SAAO,KAAK,WAAW,YAAY,QAAQ;CAC5C;CAED,MAAM,WAAWjC,IAAkD;EACjE,IAAI,UAAU,MAAM,KAAK,MAAM,WAAW,GAAG;AAC7C,MAAI,oBAAuB;AACzB,aAAU,MAAM,KAAK,WAAW,WAAW,GAAG;AAC9C,OAAI,mBACF,OAAM,KAAK,MAAM,WAAW,IAAI,QAAQ;EAE3C;AACD,SAAO;CACR;CAGD,gBAAiC;AAC/B,SAAO,KAAK,WAAW,eAAe;CACvC;CAED,MAAM,YAAY6B,UAAejB,UAAgC;AAC/D,QAAM,KAAK,WAAW,YAAY,UAAU,SAAS;AACrD,QAAM,KAAK,MAAM,YAAY,UAAU,SAAS;CACjD;CAED,MAAM,eACJiB,UACAZ,YAC4B;EAC5B,MAAM,kBAAkB,MAAM,KAAK,WAAW,eAC5C,UACA,WACD;AACD,MAAI,2BACF,OAAM,KAAK,MAAM,eAAe,UAAU,WAAW;AAEvD,SAAO;CACR;CAED,MAAM,YAAYA,YAAmC;AAEnD,MAAI,MAAM,KAAK,MAAM,YAAY,WAAW,CAC1C,QAAO;EAGT,MAAM,SAAS,MAAM,KAAK,WAAW,YAAY,WAAW;AAG5D,SAAO;CACR;CAGD,aAAaiB,SAA+D;AAG1E,SAAO,KAAK,WAAW,aAAa,QAAQ;CAC7C;CAGD,iBAAkC;AAChC,SAAO,KAAK,WAAW,gBAAgB;CACxC;CAED,MAAM,cAAclC,IAAUoB,QAA+B;AAC3D,QAAM,KAAK,WAAW,cAAc,IAAI,OAAO;AAC/C,QAAM,KAAK,MAAM,cAAc,IAAI,OAAO;CAC3C;CAED,MAAM,iBAAiBpB,IAAuC;EAC5D,MAAM,gBAAgB,MAAM,KAAK,WAAW,iBAAiB,GAAG;AAChE,MAAI,yBACF,OAAM,KAAK,MAAM,iBAAiB,GAAG;AAEvC,SAAO;CACR;CAED,MAAM,cAAcA,IAAuC;EACzD,IAAI,SAAS,MAAM,KAAK,MAAM,cAAc,GAAG;AAC/C,MAAI,mBAAsB;AACxB,YAAS,MAAM,KAAK,WAAW,cAAc,GAAG;AAChD,OAAI,kBACF,OAAM,KAAK,MAAM,cAAc,IAAI,OAAO;EAE7C;AACD,SAAO;CACR;CAED,MAAM,YAAYqB,YAAiBD,QAA+B;AAChE,QAAM,KAAK,WAAW,YAAY,YAAY,OAAO;AACrD,QAAM,KAAK,MAAM,YAAY,YAAY,OAAO;CACjD;CAED,MAAM,eAAeC,YAA8C;EACjE,MAAM,gBAAgB,MAAM,KAAK,WAAW,eAAe,WAAW;AACtE,MAAI,yBACF,OAAM,KAAK,MAAM,eAAe,WAAW;AAE7C,SAAO;CACR;CAED,MAAM,YAAYA,YAA8C;EAC9D,IAAI,SAAS,MAAM,KAAK,MAAM,YAAY,WAAW;AACrD,MAAI,mBAAsB;AACxB,YAAS,MAAM,KAAK,WAAW,YAAY,WAAW;AACtD,OAAI,kBACF,OAAM,KAAK,MAAM,YAAY,YAAY,OAAO;EAEnD;AACD,SAAO;CACR;CAED,MAAM,KAAKC,WAAiBC,SAAcC,QAA+B;AACvE,QAAM,KAAK,MAAM,KAAK,WAAW,SAAS,OAAO;AACjD,QAAM,KAAK,WAAW,KAAK,WAAW,SAAS,OAAO;CACvD;CAED,MAAM,YAAYF,WAAkC;EAClD,MAAM,SAAS,MAAM,KAAK,MAAM,YAAY,UAAU;AACtD,MAAI,SAAS,EAAG,QAAO;AACvB,SAAO,KAAK,WAAW,YAAY,UAAU;CAC9C;CAED,MAAM,WAAWA,WAA4D;EAC3E,MAAM,QAAQ,MAAM,KAAK,MAAM,WAAW,UAAU;AACpD,MAAI,WAAW,OAAO,KAAK,MAAM,CAAC,SAAS,EAAG,QAAO;AACrD,SAAO,MAAM,KAAK,WAAW,WAAW,UAAU;CACnD;AACF"}
|
|
1
|
+
{"version":3,"file":"repository.js","names":["kv: KvStore","prefixes?: KvStoreRepositoryPrefixes","keyPairs: CryptoKeyPair[]","pairs: KeyPair[]","pair: KeyPair","id: Uuid","activity: Create | Announce","messageKey: KvKey","lockKey: KvKey","listKey: KvKey","updater: (\n existing: Create | Announce,\n ) => Create | Announce | undefined | Promise<Create | Announce | undefined>","kvKey: KvKey","Create","Announce","options: RepositoryGetMessagesOptions","activity: Activity","followRequestId: URL","follower: Actor","followerKey: KvKey","followRequestKey: KvKey","actorId: URL","follower: Object","followerId: URL","options: RepositoryGetFollowersOptions","actor: Object","follow: Follow","followeeId: URL","messageId: Uuid","voterId: URL","option: string","key: KvKey","optionsKey: KvKey","result: Record<string, number>","uuid: string","followId: URL","counts: Record<string, number>","underlying: Repository","cache?: MemoryRepository","options?: RepositoryGetMessagesOptions","options?: RepositoryGetFollowersOptions"],"sources":["../src/repository.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 { KvKey, KvStore } from \"@fedify/fedify/federation\";\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\";\nexport type { KvKey, KvStore } from \"@fedify/fedify/federation\";\nexport { Announce, Create } from \"@fedify/vocab\";\n\nconst logger = getLogger([\"botkit\", \"repository\"]);\n\n/**\n * A UUID (universally unique identifier).\n * @since 0.3.0\n */\nexport type Uuid = ReturnType<typeof crypto.randomUUID>;\n\n/**\n * A repository for storing bot data.\n * @since 0.3.0\n */\nexport interface Repository {\n /**\n * Sets the key pairs of the bot actor.\n * @param keyPairs The key pairs to set.\n */\n setKeyPairs(keyPairs: CryptoKeyPair[]): Promise<void>;\n\n /**\n * Gets the key pairs of the bot actor.\n * @returns The key pairs of the bot actor. If the key pairs do not exist,\n * `undefined` will be returned.\n */\n getKeyPairs(): Promise<CryptoKeyPair[] | undefined>;\n\n /**\n * Adds a message to the repository.\n * @param id The UUID of the message.\n * @param activity The activity to add.\n */\n addMessage(id: Uuid, activity: Create | Announce): Promise<void>;\n\n /**\n * Updates a message in the repository.\n * @param id The UUID of the message.\n * @param updater The function to update the message. The function will be\n * called with the existing message, and the return value will\n * be the new message. If the function returns a promise, the\n * promise will be awaited. If the function returns either\n * `undefined` or a promise that resolves to `undefined`,\n * the message will not be updated. If the message does not\n * exist, the updater will not be called.\n * @returns `true` if the message was updated, `false` if the message does not\n * exist.\n */\n updateMessage(\n id: Uuid,\n updater: (\n existing: Create | Announce,\n ) => Create | Announce | undefined | Promise<Create | Announce | undefined>,\n ): Promise<boolean>;\n\n /**\n * Removes a message from the repository.\n * @param id The UUID of the message to remove.\n * @returns The removed activity. If the message does not exist, `undefined`\n * will be returned.\n */\n removeMessage(id: Uuid): Promise<Create | Announce | undefined>;\n\n /**\n * Gets messages from the repository.\n * @param options The options for getting messages.\n * @returns An async iterable of message activities.\n */\n getMessages(\n options?: RepositoryGetMessagesOptions,\n ): AsyncIterable<Create | Announce>;\n\n /**\n * Gets a message from the repository.\n * @param id The UUID of the message to get.\n * @returns The message activity, or `undefined` if the message does not\n * exist.\n */\n getMessage(id: Uuid): Promise<Create | Announce | undefined>;\n\n /**\n * Counts the number of messages in the repository.\n * @returns The number of messages in the repository.\n */\n countMessages(): Promise<number>;\n\n /**\n * Adds a follower to the repository.\n * @param followId The URL of the follow request.\n * @param follower The actor who follows the bot.\n */\n addFollower(followId: URL, follower: Actor): Promise<void>;\n\n /**\n * Removes a follower from the repository.\n * @param followId The URL of the follow request.\n * @param followerId The ID of the actor to remove.\n * @returns The removed actor. If the follower does not exist or the follow\n * request is not about the follower, `undefined` will be returned.\n */\n removeFollower(followId: URL, followerId: URL): Promise<Actor | undefined>;\n\n /**\n * Checks if the repository has a follower.\n * @param followerId The ID of the follower to check.\n * @returns `true` if the repository has the follower, `false` otherwise.\n */\n hasFollower(followerId: URL): Promise<boolean>;\n\n /**\n * Gets followers from the repository.\n * @param options The options for getting followers.\n * @returns An async iterable of actors who follow the bot.\n */\n getFollowers(options?: RepositoryGetFollowersOptions): AsyncIterable<Actor>;\n\n /**\n * Counts the number of followers in the repository.\n * @returns The number of followers in the repository.\n */\n countFollowers(): Promise<number>;\n\n /**\n * Adds a sent follow request to the repository.\n * @param id The UUID of the follow request.\n * @param follow The follow activity to add.\n */\n addSentFollow(id: Uuid, follow: Follow): Promise<void>;\n\n /**\n * Removes a sent follow request from the repository.\n * @param id The UUID of the follow request to remove.\n * @returns The removed follow activity. If the follow request does not\n * exist, `undefined` will be returned.\n */\n removeSentFollow(id: Uuid): Promise<Follow | undefined>;\n\n /**\n * Gets a sent follow request from the repository.\n * @param id The UUID of the follow request to get.\n * @returns The `Follow` activity, or `undefined` if the follow request does\n * not exist.\n */\n getSentFollow(id: Uuid): Promise<Follow | undefined>;\n\n /**\n * Adds a followee to the repository.\n * @param followeeId The ID of the followee to add.\n * @param follow The follow activity to add.\n */\n addFollowee(followeeId: URL, follow: Follow): Promise<void>;\n\n /**\n * Removes a followee from the repository.\n * @param followeeId The ID of the followee to remove.\n * @returns The `Follow` activity that was removed. If the followee does not\n * exist, `undefined` will be returned.\n */\n removeFollowee(followeeId: URL): Promise<Follow | undefined>;\n\n /**\n * Gets a followee from the repository.\n * @param followeeId The ID of the followee to get.\n * @returns The `Follow` activity, or `undefined` if the followee does not\n * exist.\n */\n getFollowee(followeeId: URL): Promise<Follow | undefined>;\n\n /**\n * Records a vote in a poll. If the same voter had already voted for the\n * same option in a poll, the vote will be silently ignored.\n * @param messageId The UUID of the poll message to vote on.\n * @param voterId The ID of the voter. It should be a URL of the actor who is\n * voting.\n * @param option The option that the voter is voting for. It should be one of\n * the options in the poll. If the poll allows multiple\n * selections, this should be a single option that the voter is\n * voting for, which is one of multiple calls to this method.\n * @since 0.3.0\n */\n vote(messageId: Uuid, voterId: URL, option: string): Promise<void>;\n\n /**\n * Counts the number of voters in a poll. Even if the poll allows multiple\n * selections, each voter is counted only once.\n * @param messageId The UUID of the poll message to count voters for.\n * @returns The number of voters in the poll. If the poll does not exist,\n * 0 will be returned.\n * @since 0.3.0\n */\n countVoters(messageId: Uuid): Promise<number>;\n\n /**\n * Counts the votes for each option in a poll. If the poll allows multiple\n * selections, each option is counted separately, and the same voter can\n * vote for multiple options.\n * @param messageId The UUID of the poll message to count votes for.\n * @returns A record where the keys are the options and the values are\n * the number of votes for each option. If the poll does not exist,\n * an empty record will be returned. Some options may not be\n * present in the record if no votes were cast for them.\n * @since 0.3.0\n */\n countVotes(messageId: Uuid): Promise<Readonly<Record<string, number>>>;\n}\n\n/**\n * Options for getting messages from the repository.\n * @since 0.3.0\n */\nexport interface RepositoryGetMessagesOptions {\n /**\n * The order of the messages. If omitted, `\"newest\"` will be used.\n * @default `\"newest\"`\n */\n readonly order?: \"oldest\" | \"newest\";\n\n /**\n * The timestamp to get messages created at or before this time.\n * If omitted, no limit will be applied.\n */\n readonly until?: Temporal.Instant;\n\n /**\n * The timestamp to get messages created at or after this time.\n * If omitted, no limit will be applied.\n */\n readonly since?: Temporal.Instant;\n\n /**\n * The maximum number of messages to get. If omitted, no limit will be\n * applied.\n */\n readonly limit?: number;\n}\n\n/**\n * Options for getting followers from the repository.\n * @since 0.3.0\n */\nexport interface RepositoryGetFollowersOptions {\n /**\n * The offset of the followers to get. If omitted, 0 will be used.\n * @default `0`\n */\n readonly offset?: number;\n\n /**\n * The limit of the followers to get. If omitted, no limit will be applied.\n */\n readonly limit?: number;\n}\n\n/**\n * The prefixes for key-value store keys used by the bot.\n * @since 0.3.0\n */\nexport interface KvStoreRepositoryPrefixes {\n /**\n * The key prefix used for storing the key pairs of the bot actor.\n * @default `[\"_botkit\", \"keyPairs\"]`\n */\n readonly keyPairs: KvKey;\n\n /**\n * The key prefix used for storing published messages.\n * @default `[\"_botkit\", \"messages\"]`\n */\n readonly messages: KvKey;\n\n /**\n * The key prefix used for storing followers.\n * @default `[\"_botkit\", \"followers\"]`\n */\n readonly followers: KvKey;\n\n /**\n * The key prefix used for storing incoming follow requests.\n * @default `[\"_botkit\", \"followRequests\"]`\n */\n readonly followRequests: KvKey;\n\n /**\n * The key prefix used for storing followees.\n * @default `[\"_botkit\", \"followees\"]`\n */\n readonly followees: KvKey;\n\n /**\n * The key prefix used for storing outgoing follow requests.\n * @default `[\"_botkit\", \"follows\"]`\n */\n readonly follows: KvKey;\n\n /**\n * The key prefix used for storing poll votes.\n * @default `[\"_botkit\", \"polls\"]`\n * @since 0.3.0\n */\n readonly polls: KvKey;\n}\n\n/**\n * A repository for storing bot data using a key-value store.\n */\nexport class KvRepository implements Repository {\n readonly kv: KvStore;\n readonly prefixes: KvStoreRepositoryPrefixes;\n\n /**\n * Creates a new key-value store repository.\n * @param kv The key-value store to use.\n * @param prefixes The prefixes for key-value store keys.\n */\n constructor(kv: KvStore, prefixes?: KvStoreRepositoryPrefixes) {\n if (kv.cas == null) {\n logger.warn(\n \"The given KvStore {kv} does not support CAS operations. \" +\n \"This may cause issues with concurrent updates.\",\n { kv },\n );\n }\n this.kv = kv;\n this.prefixes = {\n keyPairs: [\"_botkit\", \"keyPairs\"],\n messages: [\"_botkit\", \"messages\"],\n followers: [\"_botkit\", \"followers\"],\n followRequests: [\"_botkit\", \"followRequests\"],\n followees: [\"_botkit\", \"followees\"],\n follows: [\"_botkit\", \"follows\"],\n polls: [\"_botkit\", \"polls\"],\n ...prefixes ?? {},\n };\n }\n\n async setKeyPairs(keyPairs: CryptoKeyPair[]): Promise<void> {\n const pairs: KeyPair[] = [];\n for (const keyPair of keyPairs) {\n const pair: KeyPair = {\n private: await exportJwk(keyPair.privateKey),\n public: await exportJwk(keyPair.publicKey),\n };\n pairs.push(pair);\n }\n await this.kv.set(this.prefixes.keyPairs, pairs);\n }\n\n async getKeyPairs(): Promise<CryptoKeyPair[] | undefined> {\n const keyPairs = await this.kv.get<KeyPair[]>(this.prefixes.keyPairs);\n if (keyPairs == null) return undefined;\n const promises = keyPairs.map(async (pair) => ({\n privateKey: await importJwk(pair.private, \"private\"),\n publicKey: await importJwk(pair.public, \"public\"),\n }));\n return await Promise.all(promises);\n }\n\n async addMessage(id: Uuid, activity: Create | Announce): Promise<void> {\n const messageKey: KvKey = [...this.prefixes.messages, id];\n await this.kv.set(\n messageKey,\n await activity.toJsonLd({ format: \"compact\" }),\n );\n const lockKey: KvKey = [...this.prefixes.messages, \"lock\"];\n const listKey: KvKey = this.prefixes.messages;\n do {\n await this.kv.set(lockKey, id);\n const set = new Set(await this.kv.get<string[]>(listKey) ?? []);\n set.add(id);\n const list = [...set];\n list.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);\n await this.kv.set(listKey, list);\n } while (await this.kv.get(lockKey) !== id);\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 kvKey: KvKey = [...this.prefixes.messages, id];\n const createJson = await this.kv.get(kvKey);\n if (createJson == null) return false;\n const activity = await Activity.fromJsonLd(createJson);\n if (!(activity instanceof Create || activity instanceof Announce)) {\n return false;\n }\n const newActivity = await updater(activity);\n if (newActivity == null) return false;\n await this.kv.set(\n kvKey,\n await newActivity.toJsonLd({ format: \"compact\" }),\n );\n return true;\n }\n\n async removeMessage(id: Uuid): Promise<Create | Announce | undefined> {\n const listKey: KvKey = this.prefixes.messages;\n const lockKey: KvKey = [...listKey, \"lock\"];\n const lockId = `${id}:delete`;\n do {\n await this.kv.set(lockKey, lockId);\n const set = new Set(await this.kv.get<string[]>(listKey) ?? []);\n set.delete(id);\n const list = [...set];\n list.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);\n await this.kv.set(listKey, list);\n } while (await this.kv.get(lockKey) !== lockId);\n const messageKey: KvKey = [...listKey, id];\n const activityJson = await this.kv.get(messageKey);\n if (activityJson == null) return;\n await this.kv.delete(messageKey);\n const activity = await Activity.fromJsonLd(activityJson);\n if (activity instanceof Create || activity instanceof Announce) {\n return activity;\n }\n return undefined;\n }\n\n async *getMessages(\n options: RepositoryGetMessagesOptions = {},\n ): AsyncIterable<Create | Announce> {\n const { order, until, since, limit } = options;\n const untilTs = until == null ? null : until.epochMilliseconds;\n const sinceTs = since == null ? null : since.epochMilliseconds;\n let messageIds = await this.kv.get<string[]>(this.prefixes.messages) ?? [];\n if (sinceTs != null) {\n const offset = messageIds.findIndex((id) =>\n extractTimestamp(id) >= sinceTs\n );\n messageIds = messageIds.slice(offset);\n }\n if (untilTs != null) {\n const offset = messageIds.findLastIndex((id) =>\n extractTimestamp(id) <= untilTs\n );\n messageIds = messageIds.slice(0, offset + 1);\n }\n if (order == null || order === \"newest\") {\n messageIds = messageIds.toReversed();\n }\n if (limit != null) {\n messageIds = messageIds.slice(0, limit);\n }\n for (const id of messageIds) {\n const messageJson = await this.kv.get([...this.prefixes.messages, id]);\n if (messageJson == null) continue;\n try {\n const activity = await Activity.fromJsonLd(messageJson);\n if (activity instanceof Create || activity instanceof Announce) {\n yield activity;\n }\n } catch {\n continue;\n }\n }\n }\n\n async getMessage(id: Uuid): Promise<Create | Announce | undefined> {\n const json = await this.kv.get([...this.prefixes.messages, id]);\n if (json == null) return undefined;\n let activity: Activity;\n try {\n activity = await Activity.fromJsonLd(json);\n } catch (e) {\n if (e instanceof TypeError) return undefined;\n throw e;\n }\n if (activity instanceof Create || activity instanceof Announce) {\n return activity;\n }\n return undefined;\n }\n\n async countMessages(): Promise<number> {\n const messageIds = await this.kv.get<string[]>(this.prefixes.messages) ??\n [];\n return messageIds.length;\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 const followerKey: KvKey = [...this.prefixes.followers, follower.id.href];\n await this.kv.set(\n followerKey,\n await follower.toJsonLd({ format: \"compact\" }),\n );\n const lockKey: KvKey = [...this.prefixes.followers, \"lock\"];\n const listKey: KvKey = this.prefixes.followers;\n do {\n await this.kv.set(lockKey, follower.id.href);\n const list = await this.kv.get<string[]>(listKey) ?? [];\n if (!list.includes(follower.id.href)) list.push(follower.id.href);\n await this.kv.set(listKey, list);\n } while (await this.kv.get(lockKey) !== follower.id.href);\n const followRequestKey: KvKey = [\n ...this.prefixes.followRequests,\n followRequestId.href,\n ];\n await this.kv.set(followRequestKey, follower.id.href);\n }\n\n async removeFollower(\n followRequestId: URL,\n actorId: URL,\n ): Promise<Actor | undefined> {\n const followRequestKey: KvKey = [\n ...this.prefixes.followRequests,\n followRequestId.href,\n ];\n const followerId = await this.kv.get<string>(followRequestKey);\n if (followerId == null) return undefined;\n const followerKey: KvKey = [...this.prefixes.followers, followerId];\n if (followerId !== actorId.href) return undefined;\n const followerJson = await this.kv.get(followerKey);\n if (followerJson == null) return undefined;\n let follower: Object;\n try {\n follower = await Object.fromJsonLd(followerJson);\n } catch {\n return undefined;\n }\n if (!isActor(follower)) return undefined;\n const lockKey: KvKey = [...this.prefixes.followers, \"lock\"];\n const listKey: KvKey = this.prefixes.followers;\n do {\n await this.kv.set(lockKey, followerId);\n let list = await this.kv.get<string[]>(listKey) ?? [];\n list = list.filter((id) => id !== followerId);\n await this.kv.set(listKey, list);\n } while (await this.kv.get(lockKey) !== followerId);\n await this.kv.delete(followerKey);\n await this.kv.delete(followRequestKey);\n return follower;\n }\n\n async hasFollower(followerId: URL): Promise<boolean> {\n return await this.kv.get<unknown>([\n ...this.prefixes.followers,\n followerId.href,\n ]) != null;\n }\n\n async *getFollowers(\n options: RepositoryGetFollowersOptions = {},\n ): AsyncIterable<Actor> {\n const { offset = 0, limit } = options;\n let followerIds = await this.kv.get<string[]>(this.prefixes.followers) ??\n [];\n followerIds = followerIds.slice(offset);\n if (limit != null) {\n followerIds = followerIds.slice(0, limit);\n }\n for (const id of followerIds) {\n const json = await this.kv.get([...this.prefixes.followers, id]);\n let actor: Object;\n try {\n actor = await Object.fromJsonLd(json);\n } catch (e) {\n if (e instanceof TypeError) continue;\n throw e;\n }\n if (isActor(actor)) yield actor;\n }\n }\n\n async countFollowers(): Promise<number> {\n const followerIds = await this.kv.get<string[]>(this.prefixes.followers) ??\n [];\n return followerIds.length;\n }\n\n async addSentFollow(id: Uuid, follow: Follow): Promise<void> {\n await this.kv.set(\n [...this.prefixes.follows, id],\n await follow.toJsonLd({ format: \"compact\" }),\n );\n }\n\n async removeSentFollow(id: Uuid): Promise<Follow | undefined> {\n const follow = await this.getSentFollow(id);\n if (follow == null) return undefined;\n await this.kv.delete([...this.prefixes.follows, id]);\n return follow;\n }\n\n async getSentFollow(id: Uuid): Promise<Follow | undefined> {\n const followJson = await this.kv.get([...this.prefixes.follows, id]);\n if (followJson == null) return undefined;\n try {\n return await Follow.fromJsonLd(followJson);\n } catch {\n return undefined;\n }\n }\n\n async addFollowee(followeeId: URL, follow: Follow): Promise<void> {\n await this.kv.set(\n [...this.prefixes.followees, followeeId.href],\n await follow.toJsonLd({ format: \"compact\" }),\n );\n }\n\n async removeFollowee(followeeId: URL): Promise<Follow | undefined> {\n const follow = await this.getFollowee(followeeId);\n if (follow == null) return undefined;\n await this.kv.delete([...this.prefixes.followees, followeeId.href]);\n return follow;\n }\n\n async getFollowee(followeeId: URL): Promise<Follow | undefined> {\n const json = await this.kv.get([\n ...this.prefixes.followees,\n followeeId.href,\n ]);\n if (json == null) return undefined;\n try {\n return await Follow.fromJsonLd(json);\n } catch {\n return undefined;\n }\n }\n\n async vote(messageId: Uuid, voterId: URL, option: string): Promise<void> {\n const key: KvKey = [...this.prefixes.polls, messageId, option];\n while (true) {\n const prev = await this.kv.get<string[]>(key);\n if (prev != null && prev.includes(voterId.href)) return;\n const next = prev == null ? [voterId.href] : [...prev, voterId.href];\n if (this.kv.cas == null) {\n this.kv.set(key, next);\n break;\n } else {\n const success = await this.kv.cas(key, prev, next);\n if (success) break;\n // If the CAS operation failed, we retry to ensure the vote is recorded.\n logger.trace(\n \"CAS operation failed, retrying vote for {messageId} by {voterId} for option {option}.\",\n {\n messageId,\n voterId: voterId.href,\n option,\n },\n );\n }\n }\n const optionsKey: KvKey = [...this.prefixes.polls, messageId];\n while (true) {\n const prevOptions = await this.kv.get<string[]>(optionsKey);\n if (prevOptions != null && prevOptions.includes(option)) return;\n const nextOptions = prevOptions == null\n ? [option]\n : [...prevOptions, option];\n if (this.kv.cas == null) {\n this.kv.set(optionsKey, nextOptions);\n break;\n } else {\n const success = await this.kv.cas(optionsKey, prevOptions, nextOptions);\n if (success) break;\n // If the CAS operation failed, we retry to ensure the option is recorded.\n logger.trace(\n \"CAS operation failed, retrying to add option {option} for message {messageId}.\",\n {\n option,\n messageId,\n },\n );\n }\n }\n }\n\n async countVoters(messageId: Uuid): Promise<number> {\n const options = await this.kv.get<string[]>([\n ...this.prefixes.polls,\n messageId,\n ]) ?? [];\n const result = new Set<string>();\n for (const option of options) {\n const voters = await this.kv.get<string[]>([\n ...this.prefixes.polls,\n messageId,\n option,\n ]);\n if (voters != null) {\n for (const voter of voters) result.add(voter);\n }\n }\n return result.size;\n }\n\n async countVotes(messageId: Uuid): Promise<Readonly<Record<string, number>>> {\n const options = await this.kv.get<string[]>([\n ...this.prefixes.polls,\n messageId,\n ]) ?? [];\n const result: Record<string, number> = {};\n for (const option of options) {\n const voters = await this.kv.get<string[]>([\n ...this.prefixes.polls,\n messageId,\n option,\n ]);\n result[option] = voters == null ? 0 : voters.length;\n }\n return result;\n }\n}\n\ninterface KeyPair {\n private: JsonWebKey;\n public: JsonWebKey;\n}\n\n/**\n * Extracts the timestamp from a UUIDv7.\n * @param uuid The UUIDv7 string to extract the timestamp from.\n * @return The timestamp in milliseconds since the Unix epoch.\n * @internal\n */\nfunction extractTimestamp(uuid: string): number {\n // UUIDv7 format: xxxxxxxx-xxxx-7xxx-xxxx-xxxxxxxxxxxx\n // The timestamp is in the first 6 bytes (48 bits) of the UUID.\n if (uuid.length !== 36 || uuid[14] !== \"7\") {\n throw new TypeError(\"Invalid UUIDv7 format.\");\n }\n const timestampHex = uuid.slice(0, 8) + uuid.slice(9, 13);\n return parseInt(timestampHex, 16);\n}\n\n/**\n * A repository for storing bot data in memory. This repository is not\n * persistent and is only suitable for testing or development.\n */\nexport class MemoryRepository implements Repository {\n keyPairs?: CryptoKeyPair[];\n messages: Map<Uuid, Create | Announce> = new Map();\n followers: Map<string, Actor> = new Map();\n followRequests: Record<string, string> = {};\n sentFollows: Record<string, Follow> = {};\n followees: Record<string, Follow> = {};\n polls: Record<Uuid, Record<string, Set<string>>> = {};\n\n setKeyPairs(keyPairs: CryptoKeyPair[]): Promise<void> {\n this.keyPairs = keyPairs;\n return Promise.resolve();\n }\n\n getKeyPairs(): Promise<CryptoKeyPair[] | undefined> {\n return Promise.resolve(this.keyPairs);\n }\n\n addMessage(id: Uuid, activity: Create | Announce): Promise<void> {\n this.messages.set(id, activity);\n return Promise.resolve();\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 existing = this.messages.get(id);\n if (existing == null) return false;\n const newActivity = await updater(existing);\n if (newActivity == null) return false;\n this.messages.set(id, newActivity);\n return true;\n }\n\n removeMessage(id: Uuid): Promise<Create | Announce | undefined> {\n const activity = this.messages.get(id);\n this.messages.delete(id);\n return Promise.resolve(activity);\n }\n\n async *getMessages(\n options: RepositoryGetMessagesOptions = {},\n ): AsyncIterable<Create | Announce> {\n const { order, until, since, limit } = options;\n let messages = [...this.messages.values()];\n if (since != null) {\n messages = messages.filter((message) =>\n message.published != null &&\n Temporal.Instant.compare(message.published, since) >= 0\n );\n }\n if (until != null) {\n messages = messages.filter((message) =>\n message.published != null &&\n Temporal.Instant.compare(message.published, until) <= 0\n );\n }\n if (order === \"oldest\") {\n messages.sort((a, b) =>\n (a.published?.epochMilliseconds ?? 0) -\n (b.published?.epochMilliseconds ?? 0)\n );\n } else {\n messages.sort((a, b) =>\n (b.published?.epochMilliseconds ?? 0) -\n (a.published?.epochMilliseconds ?? 0)\n );\n }\n if (limit != null) {\n messages.slice(0, limit);\n }\n for (const message of messages) yield message;\n }\n\n getMessage(id: Uuid): Promise<Create | Announce | undefined> {\n return Promise.resolve(this.messages.get(id));\n }\n\n countMessages(): Promise<number> {\n return Promise.resolve(this.messages.size);\n }\n\n addFollower(followId: URL, follower: Actor): Promise<void> {\n if (follower.id == null) {\n throw new TypeError(\"The follower ID is missing.\");\n }\n this.followers.set(follower.id.href, follower);\n this.followRequests[followId.href] = follower.id.href;\n return Promise.resolve();\n }\n\n removeFollower(followId: URL, followerId: URL): Promise<Actor | undefined> {\n const existing = this.followRequests[followId.href];\n if (existing == null || existing !== followerId.href) {\n return Promise.resolve(undefined);\n }\n delete this.followRequests[followId.href];\n const follower = this.followers.get(followerId.href);\n this.followers.delete(followerId.href);\n return Promise.resolve(follower);\n }\n\n hasFollower(followerId: URL): Promise<boolean> {\n return Promise.resolve(this.followers.has(followerId.href));\n }\n\n async *getFollowers(\n options: RepositoryGetFollowersOptions = {},\n ): AsyncIterable<Actor> {\n const { offset = 0, limit } = options;\n let followers = [...this.followers.values()];\n followers.sort((a, b) => b.id!.href.localeCompare(a.id!.href) ?? 0);\n if (offset > 0) {\n followers = followers.slice(offset);\n }\n if (limit != null) {\n followers = followers.slice(0, limit);\n }\n for (const follower of followers) {\n yield follower;\n }\n }\n\n countFollowers(): Promise<number> {\n return Promise.resolve(this.followers.size);\n }\n\n addSentFollow(id: Uuid, follow: Follow): Promise<void> {\n this.sentFollows[id] = follow;\n return Promise.resolve();\n }\n\n removeSentFollow(id: Uuid): Promise<Follow | undefined> {\n const follow = this.sentFollows[id];\n delete this.sentFollows[id];\n return Promise.resolve(follow);\n }\n\n getSentFollow(id: Uuid): Promise<Follow | undefined> {\n return Promise.resolve(this.sentFollows[id]);\n }\n\n addFollowee(followeeId: URL, follow: Follow): Promise<void> {\n this.followees[followeeId.href] = follow;\n return Promise.resolve();\n }\n\n removeFollowee(followeeId: URL): Promise<Follow | undefined> {\n const follow = this.followees[followeeId.href];\n delete this.followees[followeeId.href];\n return Promise.resolve(follow);\n }\n\n getFollowee(followeeId: URL): Promise<Follow | undefined> {\n return Promise.resolve(this.followees[followeeId.href]);\n }\n\n vote(messageId: Uuid, voterId: URL, option: string): Promise<void> {\n const poll = this.polls[messageId] ??= {};\n const voters = poll[option] ??= new Set();\n voters.add(voterId.href);\n return Promise.resolve();\n }\n\n countVoters(messageId: Uuid): Promise<number> {\n const poll = this.polls[messageId];\n if (poll == null) return Promise.resolve(0);\n let voters = new Set<string>();\n for (const votersSet of globalThis.Object.values(poll)) {\n voters = voters.union(votersSet);\n }\n return Promise.resolve(voters.size);\n }\n\n countVotes(messageId: Uuid): Promise<Readonly<Record<string, number>>> {\n const poll = this.polls[messageId];\n if (poll == null) return Promise.resolve({});\n const counts: Record<string, number> = {};\n for (const [option, voters] of globalThis.Object.entries(poll)) {\n counts[option] = voters.size;\n }\n return Promise.resolve(counts);\n }\n}\n\n/**\n * A repository decorator that adds an in-memory cache layer on top of another\n * repository. This is useful for improving performance by reducing the number\n * of accesses to the underlying persistent storage, but it increases memory\n * usage. The cache is not persistent and will be lost when the process exits.\n *\n * Note: List operations like `getMessages` and `getFollowers`, and count\n * operations like `countMessages` and `countFollowers` are not cached and\n * always delegate to the underlying repository.\n * @since 0.3.0\n */\nexport class MemoryCachedRepository implements Repository {\n private underlying: Repository;\n private cache: MemoryRepository;\n\n /**\n * Creates a new memory-cached repository.\n * @param underlying The underlying repository to cache.\n * @param cache An optional `MemoryRepository` instance to use as the cache.\n * If not provided, a new one will be created internally.\n */\n constructor(underlying: Repository, cache?: MemoryRepository) {\n this.underlying = underlying;\n this.cache = cache ?? new MemoryRepository();\n }\n\n async setKeyPairs(keyPairs: CryptoKeyPair[]): Promise<void> {\n await this.underlying.setKeyPairs(keyPairs);\n await this.cache.setKeyPairs(keyPairs);\n }\n\n async getKeyPairs(): Promise<CryptoKeyPair[] | undefined> {\n let keyPairs = await this.cache.getKeyPairs();\n if (keyPairs === undefined) {\n keyPairs = await this.underlying.getKeyPairs();\n if (keyPairs !== undefined) await this.cache.setKeyPairs(keyPairs);\n }\n return keyPairs;\n }\n\n async addMessage(id: Uuid, activity: Create | Announce): Promise<void> {\n await this.underlying.addMessage(id, activity);\n await this.cache.addMessage(id, activity);\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 // Apply update to underlying first\n const updated = await this.underlying.updateMessage(id, updater);\n if (updated) {\n // If successful, fetch the updated message and update the cache\n const updatedMessage = await this.underlying.getMessage(id);\n if (updatedMessage) {\n await this.cache.addMessage(id, updatedMessage); // Use addMessage which acts like set\n } else {\n // Should not happen if updateMessage returned true, but handle defensively\n await this.cache.removeMessage(id);\n }\n }\n return updated;\n }\n\n async removeMessage(id: Uuid): Promise<Create | Announce | undefined> {\n const removedActivity = await this.underlying.removeMessage(id);\n if (removedActivity !== undefined) {\n await this.cache.removeMessage(id);\n }\n return removedActivity;\n }\n\n // getMessages is not cached due to complexity with options\n getMessages(\n options?: RepositoryGetMessagesOptions,\n ): AsyncIterable<Create | Announce> {\n return this.underlying.getMessages(options);\n }\n\n async getMessage(id: Uuid): Promise<Create | Announce | undefined> {\n let message = await this.cache.getMessage(id);\n if (message === undefined) {\n message = await this.underlying.getMessage(id);\n if (message !== undefined) {\n await this.cache.addMessage(id, message); // Use addMessage which acts like set\n }\n }\n return message;\n }\n\n // countMessages is not cached\n countMessages(): Promise<number> {\n return this.underlying.countMessages();\n }\n\n async addFollower(followId: URL, follower: Actor): Promise<void> {\n await this.underlying.addFollower(followId, follower);\n await this.cache.addFollower(followId, follower);\n }\n\n async removeFollower(\n followId: URL,\n followerId: URL,\n ): Promise<Actor | undefined> {\n const removedFollower = await this.underlying.removeFollower(\n followId,\n followerId,\n );\n if (removedFollower !== undefined) {\n await this.cache.removeFollower(followId, followerId);\n }\n return removedFollower;\n }\n\n async hasFollower(followerId: URL): Promise<boolean> {\n // Check cache first for potentially faster response\n if (await this.cache.hasFollower(followerId)) {\n return true;\n }\n // If not in cache, check underlying and update cache if found\n const exists = await this.underlying.hasFollower(followerId);\n // Note: We don't automatically add to cache here, as we don't have the Actor object\n // It will be cached if addFollower is called or if getFollowers iterates over it (though getFollowers isn't cached)\n return exists;\n }\n\n // getFollowers is not cached due to complexity with options\n getFollowers(options?: RepositoryGetFollowersOptions): AsyncIterable<Actor> {\n // We could potentially cache followers as they are iterated,\n // but for simplicity, delegate directly for now.\n return this.underlying.getFollowers(options);\n }\n\n // countFollowers is not cached\n countFollowers(): Promise<number> {\n return this.underlying.countFollowers();\n }\n\n async addSentFollow(id: Uuid, follow: Follow): Promise<void> {\n await this.underlying.addSentFollow(id, follow);\n await this.cache.addSentFollow(id, follow);\n }\n\n async removeSentFollow(id: Uuid): Promise<Follow | undefined> {\n const removedFollow = await this.underlying.removeSentFollow(id);\n if (removedFollow !== undefined) {\n await this.cache.removeSentFollow(id);\n }\n return removedFollow;\n }\n\n async getSentFollow(id: Uuid): Promise<Follow | undefined> {\n let follow = await this.cache.getSentFollow(id);\n if (follow === undefined) {\n follow = await this.underlying.getSentFollow(id);\n if (follow !== undefined) {\n await this.cache.addSentFollow(id, follow);\n }\n }\n return follow;\n }\n\n async addFollowee(followeeId: URL, follow: Follow): Promise<void> {\n await this.underlying.addFollowee(followeeId, follow);\n await this.cache.addFollowee(followeeId, follow);\n }\n\n async removeFollowee(followeeId: URL): Promise<Follow | undefined> {\n const removedFollow = await this.underlying.removeFollowee(followeeId);\n if (removedFollow !== undefined) {\n await this.cache.removeFollowee(followeeId);\n }\n return removedFollow;\n }\n\n async getFollowee(followeeId: URL): Promise<Follow | undefined> {\n let follow = await this.cache.getFollowee(followeeId);\n if (follow === undefined) {\n follow = await this.underlying.getFollowee(followeeId);\n if (follow !== undefined) {\n await this.cache.addFollowee(followeeId, follow);\n }\n }\n return follow;\n }\n\n async vote(messageId: Uuid, voterId: URL, option: string): Promise<void> {\n await this.cache.vote(messageId, voterId, option);\n await this.underlying.vote(messageId, voterId, option);\n }\n\n async countVoters(messageId: Uuid): Promise<number> {\n const voters = await this.cache.countVoters(messageId);\n if (voters > 0) return voters;\n return this.underlying.countVoters(messageId);\n }\n\n async countVotes(messageId: Uuid): Promise<Readonly<Record<string, number>>> {\n const votes = await this.cache.countVotes(messageId);\n if (globalThis.Object.keys(votes).length > 0) return votes;\n return await this.underlying.countVotes(messageId);\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repository.test.js","names":["factories: Record<string, () => Repository>","keyPairs: CryptoKeyPair[]","messageC"],"sources":["../src/repository.test.ts"],"sourcesContent":["// BotKit by Fedify: A framework for creating ActivityPub bots\n// Copyright (C) 2025 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 { MemoryKvStore } from \"@fedify/fedify/federation\";\nimport { importJwk } from \"@fedify/fedify/sig\";\nimport { Create, Follow, Note, Person, PUBLIC_COLLECTION } from \"@fedify/vocab\";\nimport assert from \"node:assert\";\nimport { describe, test } from \"node:test\";\nimport {\n KvRepository,\n MemoryCachedRepository,\n MemoryRepository,\n type Repository,\n} from \"./repository.ts\";\n\nfunction createKvRepository(): Repository {\n return new KvRepository(new MemoryKvStore());\n}\n\nfunction createMemoryRepository(): Repository {\n return new MemoryRepository();\n}\n\nfunction createMemoryCachedRepository(): Repository {\n return new MemoryCachedRepository(createKvRepository());\n}\n\nconst factories: Record<string, () => Repository> = {\n KvRepository: createKvRepository,\n MemoryRepository: createMemoryRepository,\n MemoryCachedRepository: createMemoryCachedRepository,\n};\n\nconst keyPairs: CryptoKeyPair[] = [\n {\n publicKey: await importJwk({\n kty: \"RSA\",\n alg: \"RS256\",\n // cSpell: disable\n n: \"1NZblYSc2beQqDmDUF_VDMeS7bUXShvIMK6NHd9OB-7ivBwad8vUcmqKwWj_ivqZva6EgD-n0549t0Pzn5xTArqEJ-c1DTyhC7TNtof0KIbU75qziHwHOqcyYCHusQgDm_TT7frDuxLqHJQ1UrdADMyCVDPFfcstPHhHp3NYStGeNcBo5B05DB_wkgqX2QF2MamQwkdRRMdZkVees38AsC6GTGoOFRI2lvJuUODtndpyjGAKOkLfkr9XzAcggRYx9ddsHBd5wylffwKhtUtWHkdVBdVAiEX8sZ38LhqNYm161PE83nfEvut6_lCCQ7DlPJ8Tp6SY-f2JTXA-C9sN0uJF8_YGhaCPgolv5Pk2UerQmvhMhql9MLDen1AvZrw0u1CWic0GQeIDA6Op9Exd5azhhdm4iKeYzAekUHFDi6WZRRZRCYgHaEEzXyFt9W3N3paolMYVOh1008d-aIgbYnZMToiwH897uQsNGkd1FVIutycXdeuhAbqB7AtLrzuD78wkKLO8k3DFcix2qaHRqiBKC3lUlDCD_I5yzinY_SOcagdpRxczvi6JN1ahUg39ZKYRtJIxUOp1H3iRrebbaOoxM19-axKH1om0sYtyX4JqYfN9QrSf3cO1I6CGnJY8hIkQ6CDH5Tmk_4VRRKdzphq4jZiiOYfR94WODPKDjTM\",\n e: \"AQAB\",\n // cSpell: enable\n key_ops: [\"verify\"],\n ext: true,\n }, \"public\"),\n privateKey: await importJwk({\n kty: \"RSA\",\n alg: \"RS256\",\n // cSpell: disable\n n: \"1NZblYSc2beQqDmDUF_VDMeS7bUXShvIMK6NHd9OB-7ivBwad8vUcmqKwWj_ivqZva6EgD-n0549t0Pzn5xTArqEJ-c1DTyhC7TNtof0KIbU75qziHwHOqcyYCHusQgDm_TT7frDuxLqHJQ1UrdADMyCVDPFfcstPHhHp3NYStGeNcBo5B05DB_wkgqX2QF2MamQwkdRRMdZkVees38AsC6GTGoOFRI2lvJuUODtndpyjGAKOkLfkr9XzAcggRYx9ddsHBd5wylffwKhtUtWHkdVBdVAiEX8sZ38LhqNYm161PE83nfEvut6_lCCQ7DlPJ8Tp6SY-f2JTXA-C9sN0uJF8_YGhaCPgolv5Pk2UerQmvhMhql9MLDen1AvZrw0u1CWic0GQeIDA6Op9Exd5azhhdm4iKeYzAekUHFDi6WZRRZRCYgHaEEzXyFt9W3N3paolMYVOh1008d-aIgbYnZMToiwH897uQsNGkd1FVIutycXdeuhAbqB7AtLrzuD78wkKLO8k3DFcix2qaHRqiBKC3lUlDCD_I5yzinY_SOcagdpRxczvi6JN1ahUg39ZKYRtJIxUOp1H3iRrebbaOoxM19-axKH1om0sYtyX4JqYfN9QrSf3cO1I6CGnJY8hIkQ6CDH5Tmk_4VRRKdzphq4jZiiOYfR94WODPKDjTM\",\n e: \"AQAB\",\n d: \"Yl3DrCHDIDhfifAyyWXRIHvoYyZL4jte1WkG3WSEOtRkRA41CWLSCCNHh8YQPNo_TdQnduJ0nTBIU7f7E6x7DQrI42xPL5Py1mc0oATLiiNurGJyUUUJTklR1e440-bhTCXmANnhtkcyngy9bEI3PvMR1PqsbswFVyo76586kjG5DhykHbGH2Ru14rk0nt23E5LLzY6Kd-AufCbjuQ-ccNC_zvdBFOn7At5-r7CVAVyhjlEgyPZ5P-hhGnG8ywxIANgUJhOPeexYL2o29IQiBBJxsCV0EsdN14UttN0etPvmRh5MRIFUE-zfRkRNQB20hMT8n4FKFlfgKkMS2gXep91h9VVyfYPHAt9jGJgUbIcbx_igeLK3nQlaUXaePf2bAuVRM1kW3P2UR0FOoUKDI5FZmi9XBoEtt0taQYySdKbPSXKaJWO2vKQ4SPyVXzzz-obfVe2zIe1msQ3Tco5RFoHfnufbvvnLC_WUAC9LSfp4jrPvr5lY3uoCFmPma56R-E3mVd2q87Ybk6mqvSh4yWHjid7sfzQ8Ovh9OhZlq_7Mfa3q3M92vNL98iHs8xYkJbE0DJs691UdgX45iNi4DVD-hJ7EbKQQgePsYNovWA611kM-cartevQWk7TBBggy9VYqmdWN0QuVQX9bsHFeYjjKSXg24bV5vYQW3EPkqZk\",\n p: \"9DeEDfMVdV605MbHCtHnw5xEbzTHd7vK-qAQNIjz5i4EmFC0tK7dvUiSn0WeyMNYJkuxVxTMHoDbWXzXq45tzbTEYuzEo5wsxyoVvldfFnnJIwMu6Hb7PWjyWfpBcbwLISr8fAJaGPzgcFsJE__KxrvLA66m1q_4k1y1L9CvXWfHDvFqb7VLGzKWXXp2wlbsACZuqx2Ff3THcWoOWb-wSww6AGsYAc3zC_DiYvAaTn9MxszZ0UYuMeJIHjLA1dmjL-Nksvq5GukjFxSSTpUS87zJ08fHoB0FzTKIIjJGpMRf6ebReLqbYCdo2Kr7eC7lbcTfwQTPI6gnHSKgPIYF5Q\",\n q: \"3xtArH_4MQjwRpl7JVivzQUZgDTARkynMpX-4Gvyny6Gxx0QLhHH0lQMRhtFWlI6qLZxCCLC9zhXPmGlqW-QWya8-xE80mX45JTrQlwBHISpTWTV3sI2Lp5dg7CW8Sc40CE4kB4Q2rHhf7V-Aimgmqhnl1uguzH2DXfr3RaCor0ge44k6gi1LXEJN_aFQIIFYL8HQOM0ctdY147Kr2rVHLchRnh8Q4GzBAJvpOcfvEDk9HF09NVxeaivLMXChpuSUHqbEGg_lVkotLnCMb-fUWk8QmO8EFFVU0pyOFDqHKIgrHOLSHjgUvV8moBwnMGQxMgu7rpY3g-9cXfsCoKVNw\",\n dp:\n \"bL1vajqrelhSGW-83r95_-pLumx4yIJwrcmpjYrRdtNUrnF5FN6r0wVGa-629dOtI1gevZSAErDzelQRP80qbSapLxcXs3XtpjzB87-5kitl-NYJA-8-jSh2iMPacgb1ua4HQDxX27p1QPH4B9SkeHrTuW8B0KQH_a2Q65pzCxcTVj7-UoEZ0SFkPHkz-fJ0INj7--soLwlTaNd9Tk8A81mdVeRZiywlpVJ7quwX-o3KJNa_weQK26FS1Udp_45pkAAjLWJgG3BldHhvcNgF2UtdXpQc-dkSZTyzyu4x8FmUD3T8HlKQrm69y4POdsQC2i6IJsy6YrkTuXBagrh2VQ\",\n dq:\n \"j0CQZjJEyjdTEAG8cF5hguKjXQ6B5qGROYnV_YNSZaMaJv8iRHJmO0Z8GwenoDbsMyfxq6emR9aFLijEleZsahqVfR-0TePry9lStWkdzZHgozD7oexRnd1Rbh0UzgLBF-I8z0x-xe0xPS7rmbfgx20aFrVentOViVBWwb6SYqvND4hVa2_r5SGPKb_AD4tsqJH_tkosgxCCmuW0fq256JYtZ3I1V6MPrqNhzCAa4GVKnSm8Tvg9xD_rOnRAUu3RJJuUtRQ6v0pgOKqNZiQDx-IqLvaa6l9OygwjCsXpjDkNga0u4Xm7j4jQWOPfasdejPt8Jwy_wtWYbiLyDE2MQQ\",\n qi:\n \"Th3TS6fHquqNljwZU2Vg7ndI0SmJidIwSTS2LlhM-Y2bxaAUF-orpS504xDVk1xjRYBrdxiTOmohbtoKtiWhLveOUAWVoNilMqgEU7lwnhaE3yfiUoE1x8df_wLP_YiAccFKeMZwsQp29aKLxuYQtO2dRSSQkN0IuxMGchnJtGOGNTbyA44O25IwggV1nlJN7OTX-nsJCSCe1XMojnGezhnD4xXGeSuR3S07oDDiWpvAO7qtRphEavVTtXdJWIr27tBvnUytbpb4uq6A3J4-TZ6X9uzlOw6jBSQhbL7fc83Z9E_wjPTnxfHufiC_AtXow6sK7lCy10aJGHp3jnGVdQ\",\n // cSpell: enable\n key_ops: [\"sign\"],\n ext: true,\n }, \"private\"),\n },\n {\n privateKey: await importJwk({\n kty: \"OKP\",\n crv: \"Ed25519\",\n // cSpell: disable-next-line\n x: \"CwcwyY7tu4wVzVW3KKX7AnBO8HakA2pg0rhAiMbGtfk\",\n key_ops: [\"sign\"],\n ext: true,\n // cSpell: disable-next-line\n d: \"K64nFsAPt892l7rr10uDsBXCW151CUM29SugU6l4ZzE\",\n }, \"private\"),\n publicKey: await importJwk({\n kty: \"OKP\",\n crv: \"Ed25519\",\n // cSpell: disable-next-line\n x: \"CwcwyY7tu4wVzVW3KKX7AnBO8HakA2pg0rhAiMbGtfk\",\n key_ops: [\"verify\"],\n ext: true,\n }, \"public\"),\n },\n];\n\nfor (const name in factories) {\n const factory = factories[name];\n\n describe(name, () => {\n const repo = factory();\n\n test(\"key pairs\", async () => {\n assert.deepStrictEqual(await repo.getKeyPairs(), undefined);\n await repo.setKeyPairs(keyPairs);\n assert.deepStrictEqual(await repo.getKeyPairs(), keyPairs);\n });\n\n test(\"messages\", async () => {\n assert.deepStrictEqual(await repo.countMessages(), 0);\n assert.deepStrictEqual(\n await repo.getMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\"),\n undefined,\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"0194244f-d800-7873-8993-ef71ccd47306\"),\n undefined,\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\"),\n undefined,\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\"),\n undefined,\n );\n assert.deepStrictEqual(await Array.fromAsync(repo.getMessages()), []);\n\n const messageA = new Create({\n id: new URL(\n \"https://example.com/ap/create/01941f29-7c00-7fe8-ab0a-7b593990a3c0\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n object: new Note({\n id: new URL(\n \"https://example.com/ap/note/01941f29-7c00-7fe8-ab0a-7b593990a3c0\",\n ),\n attribution: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n content: \"Hello, world!\",\n published: Temporal.Instant.from(\"2025-01-01T00:00:00Z\"),\n }),\n published: Temporal.Instant.from(\"2025-01-01T00:00:00Z\"),\n });\n const messageB = new Create({\n id: new URL(\n \"https://example.com/ap/create/0194244f-d800-7873-8993-ef71ccd47306\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n object: new Note({\n id: new URL(\n \"https://example.com/ap/note/0194244f-d800-7873-8993-ef71ccd47306\",\n ),\n attribution: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n content: \"Hello, world!\",\n published: Temporal.Instant.from(\"2025-01-02T00:00:00Z\"),\n }),\n published: Temporal.Instant.from(\"2025-01-02T00:00:00Z\"),\n });\n const messageC = new Create({\n id: new URL(\n \"https://example.com/ap/create/01942976-3400-7f34-872e-2cbf0f9eeac4\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n object: new Note({\n id: new URL(\n \"https://example.com/ap/note/01942976-3400-7f34-872e-2cbf0f9eeac4\",\n ),\n attribution: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n content: \"Hello, world!\",\n published: Temporal.Instant.from(\"2025-01-03T00:00:00Z\"),\n }),\n published: Temporal.Instant.from(\"2025-01-03T00:00:00Z\"),\n });\n const messageD = new Create({\n id: new URL(\n \"https://example.com/ap/create/01942e9c-9000-7480-a553-7a6ce737ce14\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n object: new Note({\n id: new URL(\n \"https://example.com/ap/note/01942e9c-9000-7480-a553-7a6ce737ce14\",\n ),\n attribution: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n content: \"Hello, world!\",\n published: Temporal.Instant.from(\"2025-01-04T00:00:00Z\"),\n }),\n published: Temporal.Instant.from(\"2025-01-04T00:00:00Z\"),\n });\n const messageC2 = new Create({\n id: new URL(\n \"https://example.com/ap/create/01942976-3400-7f34-872e-2cbf0f9eeac4\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n object: new Note({\n id: new URL(\n \"https://example.com/ap/note/01942976-3400-7f34-872e-2cbf0f9eeac4\",\n ),\n attribution: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n content: \"Hi, world!\",\n published: Temporal.Instant.from(\"2025-01-03T00:00:00Z\"),\n updated: Temporal.Instant.from(\"2025-01-03T12:00:00Z\"),\n }),\n published: Temporal.Instant.from(\"2025-01-03T00:00:00Z\"),\n updated: Temporal.Instant.from(\"2025-01-03T12:00:00Z\"),\n });\n\n await repo.addMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\", messageA);\n assert.deepStrictEqual(await repo.countMessages(), 1);\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\"))\n ?.toJsonLd(),\n await messageA.toJsonLd(),\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"0194244f-d800-7873-8993-ef71ccd47306\"),\n undefined,\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\"),\n undefined,\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\"),\n undefined,\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getMessages())).map((m) => m.toJsonLd()),\n ),\n [await messageA.toJsonLd()],\n );\n\n await repo.addMessage(\"0194244f-d800-7873-8993-ef71ccd47306\", messageB);\n assert.deepStrictEqual(await repo.countMessages(), 2);\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\"))\n ?.toJsonLd(),\n await messageA.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"0194244f-d800-7873-8993-ef71ccd47306\"))\n ?.toJsonLd(),\n await messageB.toJsonLd(),\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\"),\n undefined,\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\"),\n undefined,\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getMessages())).map((m) => m.toJsonLd()),\n ),\n [await messageB.toJsonLd(), await messageA.toJsonLd()],\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getMessages({ order: \"oldest\" }))).map((\n m,\n ) => m.toJsonLd()),\n ),\n [await messageA.toJsonLd(), await messageB.toJsonLd()],\n );\n\n await repo.addMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\", messageC);\n assert.deepStrictEqual(await repo.countMessages(), 3);\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\"))\n ?.toJsonLd(),\n await messageA.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"0194244f-d800-7873-8993-ef71ccd47306\"))\n ?.toJsonLd(),\n await messageB.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\"))\n ?.toJsonLd(),\n await messageC.toJsonLd(),\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\"),\n undefined,\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getMessages({ order: \"newest\" }))).map((\n m,\n ) => m.toJsonLd()),\n ),\n [\n await messageC.toJsonLd(),\n await messageB.toJsonLd(),\n await messageA.toJsonLd(),\n ],\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getMessages({ order: \"oldest\" }))).map((\n m,\n ) => m.toJsonLd()),\n ),\n [\n await messageA.toJsonLd(),\n await messageB.toJsonLd(),\n await messageC.toJsonLd(),\n ],\n );\n\n await repo.addMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\", messageD);\n assert.deepStrictEqual(await repo.countMessages(), 4);\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\"))\n ?.toJsonLd(),\n await messageA.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"0194244f-d800-7873-8993-ef71ccd47306\"))\n ?.toJsonLd(),\n await messageB.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\"))\n ?.toJsonLd(),\n await messageC.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\"))\n ?.toJsonLd(),\n await messageD.toJsonLd(),\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getMessages())).map((\n m,\n ) => m.toJsonLd()),\n ),\n [\n await messageD.toJsonLd(),\n await messageC.toJsonLd(),\n await messageB.toJsonLd(),\n await messageA.toJsonLd(),\n ],\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(\n repo.getMessages({\n order: \"oldest\",\n until: Temporal.Instant.from(\"2025-01-03T00:00:00Z\"),\n }),\n )).map((m) => m.toJsonLd()),\n ),\n [\n await messageA.toJsonLd(),\n await messageB.toJsonLd(),\n await messageC.toJsonLd(),\n ],\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(\n repo.getMessages({\n since: Temporal.Instant.from(\"2025-01-02T00:00:00Z\"),\n }),\n )).map((m) => m.toJsonLd()),\n ),\n [\n await messageD.toJsonLd(),\n await messageC.toJsonLd(),\n await messageB.toJsonLd(),\n ],\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(\n repo.getMessages({\n until: Temporal.Instant.from(\"2025-01-03T00:00:00Z\"),\n since: Temporal.Instant.from(\"2025-01-02T00:00:00Z\"),\n }),\n )).map((m) => m.toJsonLd()),\n ),\n [\n await messageC.toJsonLd(),\n await messageB.toJsonLd(),\n ],\n );\n\n const removed = await repo.removeMessage(\n \"0194244f-d800-7873-8993-ef71ccd47306\",\n );\n assert.deepStrictEqual(\n await removed?.toJsonLd(),\n await messageB.toJsonLd(),\n );\n assert.deepStrictEqual(await repo.countMessages(), 3);\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\"))\n ?.toJsonLd(),\n await messageA.toJsonLd(),\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"0194244f-d800-7873-8993-ef71ccd47306\"),\n undefined,\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\"))\n ?.toJsonLd(),\n await messageC.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\"))\n ?.toJsonLd(),\n await messageD.toJsonLd(),\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getMessages({ order: \"newest\" }))).map((\n m,\n ) => m.toJsonLd()),\n ),\n [\n await messageD.toJsonLd(),\n await messageC.toJsonLd(),\n await messageA.toJsonLd(),\n ],\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getMessages({ order: \"oldest\" }))).map((\n m,\n ) => m.toJsonLd()),\n ),\n [\n await messageA.toJsonLd(),\n await messageC.toJsonLd(),\n await messageD.toJsonLd(),\n ],\n );\n\n await repo.updateMessage(\n \"01942976-3400-7f34-872e-2cbf0f9eeac4\",\n async (messageC) =>\n messageC.clone({\n object: await messageC2.getObject(),\n updated: messageC2.updated,\n }),\n );\n assert.deepStrictEqual(await repo.countMessages(), 3);\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\"))\n ?.toJsonLd(),\n await messageA.toJsonLd(),\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"0194244f-d800-7873-8993-ef71ccd47306\"),\n undefined,\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\"))\n ?.toJsonLd(),\n await messageC2.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\"))\n ?.toJsonLd(),\n await messageD.toJsonLd(),\n );\n\n let updaterCalled = false;\n const updated = await repo.updateMessage(\n \"00000000-0000-0000-0000-000000000000\",\n (message) => {\n updaterCalled = true;\n return message;\n },\n );\n assert.deepStrictEqual(updated, false);\n assert.deepStrictEqual(updaterCalled, false);\n assert.deepStrictEqual(await repo.countMessages(), 3);\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\"))\n ?.toJsonLd(),\n await messageA.toJsonLd(),\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"0194244f-d800-7873-8993-ef71ccd47306\"),\n undefined,\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\"))\n ?.toJsonLd(),\n await messageC2.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\"))\n ?.toJsonLd(),\n await messageD.toJsonLd(),\n );\n\n const updated2 = await repo.updateMessage(\n \"01942e9c-9000-7480-a553-7a6ce737ce14\",\n (_) => undefined,\n );\n assert.deepStrictEqual(updated2, false);\n assert.deepStrictEqual(await repo.countMessages(), 3);\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\"))\n ?.toJsonLd(),\n await messageA.toJsonLd(),\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"0194244f-d800-7873-8993-ef71ccd47306\"),\n undefined,\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\"))\n ?.toJsonLd(),\n await messageC2.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\"))\n ?.toJsonLd(),\n await messageD.toJsonLd(),\n );\n });\n\n test(\"followers\", async () => {\n const followerA = new Person({\n id: new URL(\"https://example.com/ap/actor/john\"),\n preferredUsername: \"john\",\n });\n const followFromA = new URL(\n \"https://example.com/ap/follow/be2da56a-0ea3-4a6a-9dff-2a1837be67e0\",\n );\n const followerB = new Person({\n id: new URL(\"https://example.com/ap/actor/jane\"),\n preferredUsername: \"jane\",\n });\n const followFromB = new URL(\n \"https://example.com/ap/follow/8b76286d-5eef-4f02-8a16-080ff2b0e2ca\",\n );\n\n assert.deepStrictEqual(await repo.countFollowers(), 0);\n assert.deepStrictEqual(await repo.hasFollower(followerA.id!), false);\n assert.deepStrictEqual(await repo.hasFollower(followerB.id!), false);\n assert.deepStrictEqual(await Array.fromAsync(repo.getFollowers()), []);\n\n await repo.addFollower(followFromA, followerA);\n assert.deepStrictEqual(await repo.countFollowers(), 1);\n assert.ok(await repo.hasFollower(followerA.id!));\n assert.deepStrictEqual(await repo.hasFollower(followerB.id!), false);\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getFollowers())).map((f) => f.toJsonLd()),\n ),\n [await followerA.toJsonLd()],\n );\n\n await repo.addFollower(followFromB, followerB);\n assert.deepStrictEqual(await repo.countFollowers(), 2);\n assert.ok(await repo.hasFollower(followerA.id!));\n assert.ok(await repo.hasFollower(followerB.id!));\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getFollowers())).map((f) => f.toJsonLd()),\n ),\n [await followerA.toJsonLd(), await followerB.toJsonLd()],\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getFollowers({ offset: 1 }))).map((f) =>\n f.toJsonLd()\n ),\n ),\n [await followerB.toJsonLd()],\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getFollowers({ limit: 1 }))).map((f) =>\n f.toJsonLd()\n ),\n ),\n [await followerA.toJsonLd()],\n );\n\n assert.deepStrictEqual(\n await repo.removeFollower(followFromA, followerB.id!),\n undefined,\n );\n assert.deepStrictEqual(\n await repo.removeFollower(followFromB, followerA.id!),\n undefined,\n );\n assert.deepStrictEqual(await repo.countFollowers(), 2);\n assert.ok(await repo.hasFollower(followerA.id!));\n assert.ok(await repo.hasFollower(followerB.id!));\n\n await repo.removeFollower(followFromA, followerA.id!);\n assert.deepStrictEqual(await repo.countFollowers(), 1);\n assert.deepStrictEqual(await repo.hasFollower(followerA.id!), false);\n assert.ok(await repo.hasFollower(followerB.id!));\n\n await repo.removeFollower(followFromB, followerB.id!);\n assert.deepStrictEqual(await repo.countFollowers(), 0);\n assert.deepStrictEqual(await repo.hasFollower(followerA.id!), false);\n assert.deepStrictEqual(await repo.hasFollower(followerB.id!), false);\n });\n\n test(\"sent follows\", async () => {\n const follow = new Follow({\n id: new URL(\n \"https://example.com/ap/follow/03a395a2-353a-4894-afdb-2cab31a7b004\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n object: new URL(\"https://example.com/ap/actor/john\"),\n });\n\n assert.deepStrictEqual(\n await repo.getSentFollow(\"03a395a2-353a-4894-afdb-2cab31a7b004\"),\n undefined,\n );\n\n await repo.addSentFollow(\"03a395a2-353a-4894-afdb-2cab31a7b004\", follow);\n assert.deepStrictEqual(\n await (await repo.getSentFollow(\"03a395a2-353a-4894-afdb-2cab31a7b004\"))\n ?.toJsonLd(),\n await follow.toJsonLd(),\n );\n\n await repo.removeSentFollow(\"03a395a2-353a-4894-afdb-2cab31a7b004\");\n assert.deepStrictEqual(\n await repo.getSentFollow(\"03a395a2-353a-4894-afdb-2cab31a7b004\"),\n undefined,\n );\n });\n\n test(\"followees\", async () => {\n const followeeId = new URL(\"https://example.com/ap/actor/john\");\n const follow = new Follow({\n id: new URL(\n \"https://example.com/ap/follow/03a395a2-353a-4894-afdb-2cab31a7b004\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n object: followeeId,\n });\n\n assert.deepStrictEqual(await repo.getFollowee(followeeId), undefined);\n\n await repo.addFollowee(followeeId, follow);\n assert.deepStrictEqual(\n await (await repo.getFollowee(followeeId))?.toJsonLd(),\n await follow.toJsonLd(),\n );\n\n await repo.removeFollowee(followeeId);\n assert.deepStrictEqual(await repo.getFollowee(followeeId), undefined);\n });\n\n test(\"poll voting\", async () => {\n const messageId1 = \"01945678-1234-7890-abcd-ef0123456789\";\n const messageId2 = \"01945678-5678-7890-abcd-ef0123456789\";\n const voter1 = new URL(\"https://example.com/ap/actor/alice\");\n const voter2 = new URL(\"https://example.com/ap/actor/bob\");\n const voter3 = new URL(\"https://example.com/ap/actor/charlie\");\n\n // Initially, no votes exist\n assert.deepStrictEqual(await repo.countVoters(messageId1), 0);\n assert.deepStrictEqual(await repo.countVotes(messageId1), {});\n assert.deepStrictEqual(await repo.countVoters(messageId2), 0);\n assert.deepStrictEqual(await repo.countVotes(messageId2), {});\n\n // Single voter, single option\n await repo.vote(messageId1, voter1, \"option1\");\n assert.deepStrictEqual(await repo.countVoters(messageId1), 1);\n assert.deepStrictEqual(await repo.countVotes(messageId1), {\n \"option1\": 1,\n });\n\n // Same voter votes for same option again (should be ignored)\n await repo.vote(messageId1, voter1, \"option1\");\n assert.deepStrictEqual(await repo.countVoters(messageId1), 1);\n assert.deepStrictEqual(await repo.countVotes(messageId1), {\n \"option1\": 1,\n });\n\n // Same voter votes for different option (multiple choice)\n await repo.vote(messageId1, voter1, \"option2\");\n assert.deepStrictEqual(await repo.countVoters(messageId1), 1);\n assert.deepStrictEqual(await repo.countVotes(messageId1), {\n \"option1\": 1,\n \"option2\": 1,\n });\n\n // Different voter votes for same option\n await repo.vote(messageId1, voter2, \"option1\");\n assert.deepStrictEqual(await repo.countVoters(messageId1), 2);\n assert.deepStrictEqual(await repo.countVotes(messageId1), {\n \"option1\": 2,\n \"option2\": 1,\n });\n\n // Third voter votes for new option\n await repo.vote(messageId1, voter3, \"option3\");\n assert.deepStrictEqual(await repo.countVoters(messageId1), 3);\n assert.deepStrictEqual(await repo.countVotes(messageId1), {\n \"option1\": 2,\n \"option2\": 1,\n \"option3\": 1,\n });\n\n // Votes for different message should be separate\n await repo.vote(messageId2, voter1, \"optionA\");\n await repo.vote(messageId2, voter2, \"optionB\");\n assert.deepStrictEqual(await repo.countVoters(messageId2), 2);\n assert.deepStrictEqual(await repo.countVotes(messageId2), {\n \"optionA\": 1,\n \"optionB\": 1,\n });\n\n // Original message votes should remain unchanged\n assert.deepStrictEqual(await repo.countVoters(messageId1), 3);\n assert.deepStrictEqual(await repo.countVotes(messageId1), {\n \"option1\": 2,\n \"option2\": 1,\n \"option3\": 1,\n });\n\n // Test with empty options (edge case)\n await repo.vote(messageId1, voter1, \"\");\n assert.deepStrictEqual(await repo.countVoters(messageId1), 3);\n assert.deepStrictEqual(await repo.countVotes(messageId1), {\n \"option1\": 2,\n \"option2\": 1,\n \"option3\": 1,\n \"\": 1,\n });\n });\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"repository.test.js","names":["factories: Record<string, () => Repository>","keyPairs: CryptoKeyPair[]","messageC"],"sources":["../src/repository.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 { MemoryKvStore } from \"@fedify/fedify/federation\";\nimport { importJwk } from \"@fedify/fedify/sig\";\nimport { Create, Follow, Note, Person, PUBLIC_COLLECTION } from \"@fedify/vocab\";\nimport assert from \"node:assert\";\nimport { describe, test } from \"node:test\";\nimport {\n KvRepository,\n MemoryCachedRepository,\n MemoryRepository,\n type Repository,\n} from \"./repository.ts\";\n\nfunction createKvRepository(): Repository {\n return new KvRepository(new MemoryKvStore());\n}\n\nfunction createMemoryRepository(): Repository {\n return new MemoryRepository();\n}\n\nfunction createMemoryCachedRepository(): Repository {\n return new MemoryCachedRepository(createKvRepository());\n}\n\nconst factories: Record<string, () => Repository> = {\n KvRepository: createKvRepository,\n MemoryRepository: createMemoryRepository,\n MemoryCachedRepository: createMemoryCachedRepository,\n};\n\nconst keyPairs: CryptoKeyPair[] = [\n {\n publicKey: await importJwk({\n kty: \"RSA\",\n alg: \"RS256\",\n // cSpell: disable\n n: \"1NZblYSc2beQqDmDUF_VDMeS7bUXShvIMK6NHd9OB-7ivBwad8vUcmqKwWj_ivqZva6EgD-n0549t0Pzn5xTArqEJ-c1DTyhC7TNtof0KIbU75qziHwHOqcyYCHusQgDm_TT7frDuxLqHJQ1UrdADMyCVDPFfcstPHhHp3NYStGeNcBo5B05DB_wkgqX2QF2MamQwkdRRMdZkVees38AsC6GTGoOFRI2lvJuUODtndpyjGAKOkLfkr9XzAcggRYx9ddsHBd5wylffwKhtUtWHkdVBdVAiEX8sZ38LhqNYm161PE83nfEvut6_lCCQ7DlPJ8Tp6SY-f2JTXA-C9sN0uJF8_YGhaCPgolv5Pk2UerQmvhMhql9MLDen1AvZrw0u1CWic0GQeIDA6Op9Exd5azhhdm4iKeYzAekUHFDi6WZRRZRCYgHaEEzXyFt9W3N3paolMYVOh1008d-aIgbYnZMToiwH897uQsNGkd1FVIutycXdeuhAbqB7AtLrzuD78wkKLO8k3DFcix2qaHRqiBKC3lUlDCD_I5yzinY_SOcagdpRxczvi6JN1ahUg39ZKYRtJIxUOp1H3iRrebbaOoxM19-axKH1om0sYtyX4JqYfN9QrSf3cO1I6CGnJY8hIkQ6CDH5Tmk_4VRRKdzphq4jZiiOYfR94WODPKDjTM\",\n e: \"AQAB\",\n // cSpell: enable\n key_ops: [\"verify\"],\n ext: true,\n }, \"public\"),\n privateKey: await importJwk({\n kty: \"RSA\",\n alg: \"RS256\",\n // cSpell: disable\n n: \"1NZblYSc2beQqDmDUF_VDMeS7bUXShvIMK6NHd9OB-7ivBwad8vUcmqKwWj_ivqZva6EgD-n0549t0Pzn5xTArqEJ-c1DTyhC7TNtof0KIbU75qziHwHOqcyYCHusQgDm_TT7frDuxLqHJQ1UrdADMyCVDPFfcstPHhHp3NYStGeNcBo5B05DB_wkgqX2QF2MamQwkdRRMdZkVees38AsC6GTGoOFRI2lvJuUODtndpyjGAKOkLfkr9XzAcggRYx9ddsHBd5wylffwKhtUtWHkdVBdVAiEX8sZ38LhqNYm161PE83nfEvut6_lCCQ7DlPJ8Tp6SY-f2JTXA-C9sN0uJF8_YGhaCPgolv5Pk2UerQmvhMhql9MLDen1AvZrw0u1CWic0GQeIDA6Op9Exd5azhhdm4iKeYzAekUHFDi6WZRRZRCYgHaEEzXyFt9W3N3paolMYVOh1008d-aIgbYnZMToiwH897uQsNGkd1FVIutycXdeuhAbqB7AtLrzuD78wkKLO8k3DFcix2qaHRqiBKC3lUlDCD_I5yzinY_SOcagdpRxczvi6JN1ahUg39ZKYRtJIxUOp1H3iRrebbaOoxM19-axKH1om0sYtyX4JqYfN9QrSf3cO1I6CGnJY8hIkQ6CDH5Tmk_4VRRKdzphq4jZiiOYfR94WODPKDjTM\",\n e: \"AQAB\",\n d: \"Yl3DrCHDIDhfifAyyWXRIHvoYyZL4jte1WkG3WSEOtRkRA41CWLSCCNHh8YQPNo_TdQnduJ0nTBIU7f7E6x7DQrI42xPL5Py1mc0oATLiiNurGJyUUUJTklR1e440-bhTCXmANnhtkcyngy9bEI3PvMR1PqsbswFVyo76586kjG5DhykHbGH2Ru14rk0nt23E5LLzY6Kd-AufCbjuQ-ccNC_zvdBFOn7At5-r7CVAVyhjlEgyPZ5P-hhGnG8ywxIANgUJhOPeexYL2o29IQiBBJxsCV0EsdN14UttN0etPvmRh5MRIFUE-zfRkRNQB20hMT8n4FKFlfgKkMS2gXep91h9VVyfYPHAt9jGJgUbIcbx_igeLK3nQlaUXaePf2bAuVRM1kW3P2UR0FOoUKDI5FZmi9XBoEtt0taQYySdKbPSXKaJWO2vKQ4SPyVXzzz-obfVe2zIe1msQ3Tco5RFoHfnufbvvnLC_WUAC9LSfp4jrPvr5lY3uoCFmPma56R-E3mVd2q87Ybk6mqvSh4yWHjid7sfzQ8Ovh9OhZlq_7Mfa3q3M92vNL98iHs8xYkJbE0DJs691UdgX45iNi4DVD-hJ7EbKQQgePsYNovWA611kM-cartevQWk7TBBggy9VYqmdWN0QuVQX9bsHFeYjjKSXg24bV5vYQW3EPkqZk\",\n p: \"9DeEDfMVdV605MbHCtHnw5xEbzTHd7vK-qAQNIjz5i4EmFC0tK7dvUiSn0WeyMNYJkuxVxTMHoDbWXzXq45tzbTEYuzEo5wsxyoVvldfFnnJIwMu6Hb7PWjyWfpBcbwLISr8fAJaGPzgcFsJE__KxrvLA66m1q_4k1y1L9CvXWfHDvFqb7VLGzKWXXp2wlbsACZuqx2Ff3THcWoOWb-wSww6AGsYAc3zC_DiYvAaTn9MxszZ0UYuMeJIHjLA1dmjL-Nksvq5GukjFxSSTpUS87zJ08fHoB0FzTKIIjJGpMRf6ebReLqbYCdo2Kr7eC7lbcTfwQTPI6gnHSKgPIYF5Q\",\n q: \"3xtArH_4MQjwRpl7JVivzQUZgDTARkynMpX-4Gvyny6Gxx0QLhHH0lQMRhtFWlI6qLZxCCLC9zhXPmGlqW-QWya8-xE80mX45JTrQlwBHISpTWTV3sI2Lp5dg7CW8Sc40CE4kB4Q2rHhf7V-Aimgmqhnl1uguzH2DXfr3RaCor0ge44k6gi1LXEJN_aFQIIFYL8HQOM0ctdY147Kr2rVHLchRnh8Q4GzBAJvpOcfvEDk9HF09NVxeaivLMXChpuSUHqbEGg_lVkotLnCMb-fUWk8QmO8EFFVU0pyOFDqHKIgrHOLSHjgUvV8moBwnMGQxMgu7rpY3g-9cXfsCoKVNw\",\n dp:\n \"bL1vajqrelhSGW-83r95_-pLumx4yIJwrcmpjYrRdtNUrnF5FN6r0wVGa-629dOtI1gevZSAErDzelQRP80qbSapLxcXs3XtpjzB87-5kitl-NYJA-8-jSh2iMPacgb1ua4HQDxX27p1QPH4B9SkeHrTuW8B0KQH_a2Q65pzCxcTVj7-UoEZ0SFkPHkz-fJ0INj7--soLwlTaNd9Tk8A81mdVeRZiywlpVJ7quwX-o3KJNa_weQK26FS1Udp_45pkAAjLWJgG3BldHhvcNgF2UtdXpQc-dkSZTyzyu4x8FmUD3T8HlKQrm69y4POdsQC2i6IJsy6YrkTuXBagrh2VQ\",\n dq:\n \"j0CQZjJEyjdTEAG8cF5hguKjXQ6B5qGROYnV_YNSZaMaJv8iRHJmO0Z8GwenoDbsMyfxq6emR9aFLijEleZsahqVfR-0TePry9lStWkdzZHgozD7oexRnd1Rbh0UzgLBF-I8z0x-xe0xPS7rmbfgx20aFrVentOViVBWwb6SYqvND4hVa2_r5SGPKb_AD4tsqJH_tkosgxCCmuW0fq256JYtZ3I1V6MPrqNhzCAa4GVKnSm8Tvg9xD_rOnRAUu3RJJuUtRQ6v0pgOKqNZiQDx-IqLvaa6l9OygwjCsXpjDkNga0u4Xm7j4jQWOPfasdejPt8Jwy_wtWYbiLyDE2MQQ\",\n qi:\n \"Th3TS6fHquqNljwZU2Vg7ndI0SmJidIwSTS2LlhM-Y2bxaAUF-orpS504xDVk1xjRYBrdxiTOmohbtoKtiWhLveOUAWVoNilMqgEU7lwnhaE3yfiUoE1x8df_wLP_YiAccFKeMZwsQp29aKLxuYQtO2dRSSQkN0IuxMGchnJtGOGNTbyA44O25IwggV1nlJN7OTX-nsJCSCe1XMojnGezhnD4xXGeSuR3S07oDDiWpvAO7qtRphEavVTtXdJWIr27tBvnUytbpb4uq6A3J4-TZ6X9uzlOw6jBSQhbL7fc83Z9E_wjPTnxfHufiC_AtXow6sK7lCy10aJGHp3jnGVdQ\",\n // cSpell: enable\n key_ops: [\"sign\"],\n ext: true,\n }, \"private\"),\n },\n {\n privateKey: await importJwk({\n kty: \"OKP\",\n crv: \"Ed25519\",\n // cSpell: disable-next-line\n x: \"CwcwyY7tu4wVzVW3KKX7AnBO8HakA2pg0rhAiMbGtfk\",\n key_ops: [\"sign\"],\n ext: true,\n // cSpell: disable-next-line\n d: \"K64nFsAPt892l7rr10uDsBXCW151CUM29SugU6l4ZzE\",\n }, \"private\"),\n publicKey: await importJwk({\n kty: \"OKP\",\n crv: \"Ed25519\",\n // cSpell: disable-next-line\n x: \"CwcwyY7tu4wVzVW3KKX7AnBO8HakA2pg0rhAiMbGtfk\",\n key_ops: [\"verify\"],\n ext: true,\n }, \"public\"),\n },\n];\n\nfor (const name in factories) {\n const factory = factories[name];\n\n describe(name, () => {\n const repo = factory();\n\n test(\"key pairs\", async () => {\n assert.deepStrictEqual(await repo.getKeyPairs(), undefined);\n await repo.setKeyPairs(keyPairs);\n assert.deepStrictEqual(await repo.getKeyPairs(), keyPairs);\n });\n\n test(\"messages\", async () => {\n assert.deepStrictEqual(await repo.countMessages(), 0);\n assert.deepStrictEqual(\n await repo.getMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\"),\n undefined,\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"0194244f-d800-7873-8993-ef71ccd47306\"),\n undefined,\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\"),\n undefined,\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\"),\n undefined,\n );\n assert.deepStrictEqual(await Array.fromAsync(repo.getMessages()), []);\n\n const messageA = new Create({\n id: new URL(\n \"https://example.com/ap/create/01941f29-7c00-7fe8-ab0a-7b593990a3c0\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n object: new Note({\n id: new URL(\n \"https://example.com/ap/note/01941f29-7c00-7fe8-ab0a-7b593990a3c0\",\n ),\n attribution: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n content: \"Hello, world!\",\n published: Temporal.Instant.from(\"2025-01-01T00:00:00Z\"),\n }),\n published: Temporal.Instant.from(\"2025-01-01T00:00:00Z\"),\n });\n const messageB = new Create({\n id: new URL(\n \"https://example.com/ap/create/0194244f-d800-7873-8993-ef71ccd47306\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n object: new Note({\n id: new URL(\n \"https://example.com/ap/note/0194244f-d800-7873-8993-ef71ccd47306\",\n ),\n attribution: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n content: \"Hello, world!\",\n published: Temporal.Instant.from(\"2025-01-02T00:00:00Z\"),\n }),\n published: Temporal.Instant.from(\"2025-01-02T00:00:00Z\"),\n });\n const messageC = new Create({\n id: new URL(\n \"https://example.com/ap/create/01942976-3400-7f34-872e-2cbf0f9eeac4\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n object: new Note({\n id: new URL(\n \"https://example.com/ap/note/01942976-3400-7f34-872e-2cbf0f9eeac4\",\n ),\n attribution: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n content: \"Hello, world!\",\n published: Temporal.Instant.from(\"2025-01-03T00:00:00Z\"),\n }),\n published: Temporal.Instant.from(\"2025-01-03T00:00:00Z\"),\n });\n const messageD = new Create({\n id: new URL(\n \"https://example.com/ap/create/01942e9c-9000-7480-a553-7a6ce737ce14\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n object: new Note({\n id: new URL(\n \"https://example.com/ap/note/01942e9c-9000-7480-a553-7a6ce737ce14\",\n ),\n attribution: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n content: \"Hello, world!\",\n published: Temporal.Instant.from(\"2025-01-04T00:00:00Z\"),\n }),\n published: Temporal.Instant.from(\"2025-01-04T00:00:00Z\"),\n });\n const messageC2 = new Create({\n id: new URL(\n \"https://example.com/ap/create/01942976-3400-7f34-872e-2cbf0f9eeac4\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n object: new Note({\n id: new URL(\n \"https://example.com/ap/note/01942976-3400-7f34-872e-2cbf0f9eeac4\",\n ),\n attribution: new URL(\"https://example.com/ap/actor/bot\"),\n to: new URL(\"https://example.com/ap/actor/bot/followers\"),\n cc: PUBLIC_COLLECTION,\n content: \"Hi, world!\",\n published: Temporal.Instant.from(\"2025-01-03T00:00:00Z\"),\n updated: Temporal.Instant.from(\"2025-01-03T12:00:00Z\"),\n }),\n published: Temporal.Instant.from(\"2025-01-03T00:00:00Z\"),\n updated: Temporal.Instant.from(\"2025-01-03T12:00:00Z\"),\n });\n\n await repo.addMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\", messageA);\n assert.deepStrictEqual(await repo.countMessages(), 1);\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\"))\n ?.toJsonLd(),\n await messageA.toJsonLd(),\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"0194244f-d800-7873-8993-ef71ccd47306\"),\n undefined,\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\"),\n undefined,\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\"),\n undefined,\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getMessages())).map((m) => m.toJsonLd()),\n ),\n [await messageA.toJsonLd()],\n );\n\n await repo.addMessage(\"0194244f-d800-7873-8993-ef71ccd47306\", messageB);\n assert.deepStrictEqual(await repo.countMessages(), 2);\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\"))\n ?.toJsonLd(),\n await messageA.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"0194244f-d800-7873-8993-ef71ccd47306\"))\n ?.toJsonLd(),\n await messageB.toJsonLd(),\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\"),\n undefined,\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\"),\n undefined,\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getMessages())).map((m) => m.toJsonLd()),\n ),\n [await messageB.toJsonLd(), await messageA.toJsonLd()],\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getMessages({ order: \"oldest\" }))).map((\n m,\n ) => m.toJsonLd()),\n ),\n [await messageA.toJsonLd(), await messageB.toJsonLd()],\n );\n\n await repo.addMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\", messageC);\n assert.deepStrictEqual(await repo.countMessages(), 3);\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\"))\n ?.toJsonLd(),\n await messageA.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"0194244f-d800-7873-8993-ef71ccd47306\"))\n ?.toJsonLd(),\n await messageB.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\"))\n ?.toJsonLd(),\n await messageC.toJsonLd(),\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\"),\n undefined,\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getMessages({ order: \"newest\" }))).map((\n m,\n ) => m.toJsonLd()),\n ),\n [\n await messageC.toJsonLd(),\n await messageB.toJsonLd(),\n await messageA.toJsonLd(),\n ],\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getMessages({ order: \"oldest\" }))).map((\n m,\n ) => m.toJsonLd()),\n ),\n [\n await messageA.toJsonLd(),\n await messageB.toJsonLd(),\n await messageC.toJsonLd(),\n ],\n );\n\n await repo.addMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\", messageD);\n assert.deepStrictEqual(await repo.countMessages(), 4);\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\"))\n ?.toJsonLd(),\n await messageA.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"0194244f-d800-7873-8993-ef71ccd47306\"))\n ?.toJsonLd(),\n await messageB.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\"))\n ?.toJsonLd(),\n await messageC.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\"))\n ?.toJsonLd(),\n await messageD.toJsonLd(),\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getMessages())).map((\n m,\n ) => m.toJsonLd()),\n ),\n [\n await messageD.toJsonLd(),\n await messageC.toJsonLd(),\n await messageB.toJsonLd(),\n await messageA.toJsonLd(),\n ],\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(\n repo.getMessages({\n order: \"oldest\",\n until: Temporal.Instant.from(\"2025-01-03T00:00:00Z\"),\n }),\n )).map((m) => m.toJsonLd()),\n ),\n [\n await messageA.toJsonLd(),\n await messageB.toJsonLd(),\n await messageC.toJsonLd(),\n ],\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(\n repo.getMessages({\n since: Temporal.Instant.from(\"2025-01-02T00:00:00Z\"),\n }),\n )).map((m) => m.toJsonLd()),\n ),\n [\n await messageD.toJsonLd(),\n await messageC.toJsonLd(),\n await messageB.toJsonLd(),\n ],\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(\n repo.getMessages({\n until: Temporal.Instant.from(\"2025-01-03T00:00:00Z\"),\n since: Temporal.Instant.from(\"2025-01-02T00:00:00Z\"),\n }),\n )).map((m) => m.toJsonLd()),\n ),\n [\n await messageC.toJsonLd(),\n await messageB.toJsonLd(),\n ],\n );\n\n const removed = await repo.removeMessage(\n \"0194244f-d800-7873-8993-ef71ccd47306\",\n );\n assert.deepStrictEqual(\n await removed?.toJsonLd(),\n await messageB.toJsonLd(),\n );\n assert.deepStrictEqual(await repo.countMessages(), 3);\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\"))\n ?.toJsonLd(),\n await messageA.toJsonLd(),\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"0194244f-d800-7873-8993-ef71ccd47306\"),\n undefined,\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\"))\n ?.toJsonLd(),\n await messageC.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\"))\n ?.toJsonLd(),\n await messageD.toJsonLd(),\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getMessages({ order: \"newest\" }))).map((\n m,\n ) => m.toJsonLd()),\n ),\n [\n await messageD.toJsonLd(),\n await messageC.toJsonLd(),\n await messageA.toJsonLd(),\n ],\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getMessages({ order: \"oldest\" }))).map((\n m,\n ) => m.toJsonLd()),\n ),\n [\n await messageA.toJsonLd(),\n await messageC.toJsonLd(),\n await messageD.toJsonLd(),\n ],\n );\n\n await repo.updateMessage(\n \"01942976-3400-7f34-872e-2cbf0f9eeac4\",\n async (messageC) =>\n messageC.clone({\n object: await messageC2.getObject(),\n updated: messageC2.updated,\n }),\n );\n assert.deepStrictEqual(await repo.countMessages(), 3);\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\"))\n ?.toJsonLd(),\n await messageA.toJsonLd(),\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"0194244f-d800-7873-8993-ef71ccd47306\"),\n undefined,\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\"))\n ?.toJsonLd(),\n await messageC2.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\"))\n ?.toJsonLd(),\n await messageD.toJsonLd(),\n );\n\n let updaterCalled = false;\n const updated = await repo.updateMessage(\n \"00000000-0000-0000-0000-000000000000\",\n (message) => {\n updaterCalled = true;\n return message;\n },\n );\n assert.deepStrictEqual(updated, false);\n assert.deepStrictEqual(updaterCalled, false);\n assert.deepStrictEqual(await repo.countMessages(), 3);\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\"))\n ?.toJsonLd(),\n await messageA.toJsonLd(),\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"0194244f-d800-7873-8993-ef71ccd47306\"),\n undefined,\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\"))\n ?.toJsonLd(),\n await messageC2.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\"))\n ?.toJsonLd(),\n await messageD.toJsonLd(),\n );\n\n const updated2 = await repo.updateMessage(\n \"01942e9c-9000-7480-a553-7a6ce737ce14\",\n (_) => undefined,\n );\n assert.deepStrictEqual(updated2, false);\n assert.deepStrictEqual(await repo.countMessages(), 3);\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\"))\n ?.toJsonLd(),\n await messageA.toJsonLd(),\n );\n assert.deepStrictEqual(\n await repo.getMessage(\"0194244f-d800-7873-8993-ef71ccd47306\"),\n undefined,\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\"))\n ?.toJsonLd(),\n await messageC2.toJsonLd(),\n );\n assert.deepStrictEqual(\n await (await repo.getMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\"))\n ?.toJsonLd(),\n await messageD.toJsonLd(),\n );\n });\n\n test(\"followers\", async () => {\n const followerA = new Person({\n id: new URL(\"https://example.com/ap/actor/john\"),\n preferredUsername: \"john\",\n });\n const followFromA = new URL(\n \"https://example.com/ap/follow/be2da56a-0ea3-4a6a-9dff-2a1837be67e0\",\n );\n const followerB = new Person({\n id: new URL(\"https://example.com/ap/actor/jane\"),\n preferredUsername: \"jane\",\n });\n const followFromB = new URL(\n \"https://example.com/ap/follow/8b76286d-5eef-4f02-8a16-080ff2b0e2ca\",\n );\n\n assert.deepStrictEqual(await repo.countFollowers(), 0);\n assert.deepStrictEqual(await repo.hasFollower(followerA.id!), false);\n assert.deepStrictEqual(await repo.hasFollower(followerB.id!), false);\n assert.deepStrictEqual(await Array.fromAsync(repo.getFollowers()), []);\n\n await repo.addFollower(followFromA, followerA);\n assert.deepStrictEqual(await repo.countFollowers(), 1);\n assert.ok(await repo.hasFollower(followerA.id!));\n assert.deepStrictEqual(await repo.hasFollower(followerB.id!), false);\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getFollowers())).map((f) => f.toJsonLd()),\n ),\n [await followerA.toJsonLd()],\n );\n\n await repo.addFollower(followFromB, followerB);\n assert.deepStrictEqual(await repo.countFollowers(), 2);\n assert.ok(await repo.hasFollower(followerA.id!));\n assert.ok(await repo.hasFollower(followerB.id!));\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getFollowers())).map((f) => f.toJsonLd()),\n ),\n [await followerA.toJsonLd(), await followerB.toJsonLd()],\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getFollowers({ offset: 1 }))).map((f) =>\n f.toJsonLd()\n ),\n ),\n [await followerB.toJsonLd()],\n );\n assert.deepStrictEqual(\n await Promise.all(\n (await Array.fromAsync(repo.getFollowers({ limit: 1 }))).map((f) =>\n f.toJsonLd()\n ),\n ),\n [await followerA.toJsonLd()],\n );\n\n assert.deepStrictEqual(\n await repo.removeFollower(followFromA, followerB.id!),\n undefined,\n );\n assert.deepStrictEqual(\n await repo.removeFollower(followFromB, followerA.id!),\n undefined,\n );\n assert.deepStrictEqual(await repo.countFollowers(), 2);\n assert.ok(await repo.hasFollower(followerA.id!));\n assert.ok(await repo.hasFollower(followerB.id!));\n\n await repo.removeFollower(followFromA, followerA.id!);\n assert.deepStrictEqual(await repo.countFollowers(), 1);\n assert.deepStrictEqual(await repo.hasFollower(followerA.id!), false);\n assert.ok(await repo.hasFollower(followerB.id!));\n\n await repo.removeFollower(followFromB, followerB.id!);\n assert.deepStrictEqual(await repo.countFollowers(), 0);\n assert.deepStrictEqual(await repo.hasFollower(followerA.id!), false);\n assert.deepStrictEqual(await repo.hasFollower(followerB.id!), false);\n });\n\n test(\"sent follows\", async () => {\n const follow = new Follow({\n id: new URL(\n \"https://example.com/ap/follow/03a395a2-353a-4894-afdb-2cab31a7b004\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n object: new URL(\"https://example.com/ap/actor/john\"),\n });\n\n assert.deepStrictEqual(\n await repo.getSentFollow(\"03a395a2-353a-4894-afdb-2cab31a7b004\"),\n undefined,\n );\n\n await repo.addSentFollow(\"03a395a2-353a-4894-afdb-2cab31a7b004\", follow);\n assert.deepStrictEqual(\n await (await repo.getSentFollow(\"03a395a2-353a-4894-afdb-2cab31a7b004\"))\n ?.toJsonLd(),\n await follow.toJsonLd(),\n );\n\n await repo.removeSentFollow(\"03a395a2-353a-4894-afdb-2cab31a7b004\");\n assert.deepStrictEqual(\n await repo.getSentFollow(\"03a395a2-353a-4894-afdb-2cab31a7b004\"),\n undefined,\n );\n });\n\n test(\"followees\", async () => {\n const followeeId = new URL(\"https://example.com/ap/actor/john\");\n const follow = new Follow({\n id: new URL(\n \"https://example.com/ap/follow/03a395a2-353a-4894-afdb-2cab31a7b004\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n object: followeeId,\n });\n\n assert.deepStrictEqual(await repo.getFollowee(followeeId), undefined);\n\n await repo.addFollowee(followeeId, follow);\n assert.deepStrictEqual(\n await (await repo.getFollowee(followeeId))?.toJsonLd(),\n await follow.toJsonLd(),\n );\n\n await repo.removeFollowee(followeeId);\n assert.deepStrictEqual(await repo.getFollowee(followeeId), undefined);\n });\n\n test(\"poll voting\", async () => {\n const messageId1 = \"01945678-1234-7890-abcd-ef0123456789\";\n const messageId2 = \"01945678-5678-7890-abcd-ef0123456789\";\n const voter1 = new URL(\"https://example.com/ap/actor/alice\");\n const voter2 = new URL(\"https://example.com/ap/actor/bob\");\n const voter3 = new URL(\"https://example.com/ap/actor/charlie\");\n\n // Initially, no votes exist\n assert.deepStrictEqual(await repo.countVoters(messageId1), 0);\n assert.deepStrictEqual(await repo.countVotes(messageId1), {});\n assert.deepStrictEqual(await repo.countVoters(messageId2), 0);\n assert.deepStrictEqual(await repo.countVotes(messageId2), {});\n\n // Single voter, single option\n await repo.vote(messageId1, voter1, \"option1\");\n assert.deepStrictEqual(await repo.countVoters(messageId1), 1);\n assert.deepStrictEqual(await repo.countVotes(messageId1), {\n \"option1\": 1,\n });\n\n // Same voter votes for same option again (should be ignored)\n await repo.vote(messageId1, voter1, \"option1\");\n assert.deepStrictEqual(await repo.countVoters(messageId1), 1);\n assert.deepStrictEqual(await repo.countVotes(messageId1), {\n \"option1\": 1,\n });\n\n // Same voter votes for different option (multiple choice)\n await repo.vote(messageId1, voter1, \"option2\");\n assert.deepStrictEqual(await repo.countVoters(messageId1), 1);\n assert.deepStrictEqual(await repo.countVotes(messageId1), {\n \"option1\": 1,\n \"option2\": 1,\n });\n\n // Different voter votes for same option\n await repo.vote(messageId1, voter2, \"option1\");\n assert.deepStrictEqual(await repo.countVoters(messageId1), 2);\n assert.deepStrictEqual(await repo.countVotes(messageId1), {\n \"option1\": 2,\n \"option2\": 1,\n });\n\n // Third voter votes for new option\n await repo.vote(messageId1, voter3, \"option3\");\n assert.deepStrictEqual(await repo.countVoters(messageId1), 3);\n assert.deepStrictEqual(await repo.countVotes(messageId1), {\n \"option1\": 2,\n \"option2\": 1,\n \"option3\": 1,\n });\n\n // Votes for different message should be separate\n await repo.vote(messageId2, voter1, \"optionA\");\n await repo.vote(messageId2, voter2, \"optionB\");\n assert.deepStrictEqual(await repo.countVoters(messageId2), 2);\n assert.deepStrictEqual(await repo.countVotes(messageId2), {\n \"optionA\": 1,\n \"optionB\": 1,\n });\n\n // Original message votes should remain unchanged\n assert.deepStrictEqual(await repo.countVoters(messageId1), 3);\n assert.deepStrictEqual(await repo.countVotes(messageId1), {\n \"option1\": 2,\n \"option2\": 1,\n \"option3\": 1,\n });\n\n // Test with empty options (edge case)\n await repo.vote(messageId1, voter1, \"\");\n assert.deepStrictEqual(await repo.countVoters(messageId1), 3);\n assert.deepStrictEqual(await repo.countVotes(messageId1), {\n \"option1\": 2,\n \"option2\": 1,\n \"option3\": 1,\n \"\": 1,\n });\n });\n });\n}\n"],"mappings":"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"}
|