@fedify/botkit 0.4.0-dev.186 → 0.4.0-dev.193
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.d.ts +2 -2
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-impl.test.js","names":["bot: BotImpl<void>","origin: URL | string"],"sources":["../src/session-impl.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 { type Context, MemoryKvStore } from \"@fedify/fedify/federation\";\nimport {\n type Activity,\n Create,\n Follow,\n Note,\n Person,\n PUBLIC_COLLECTION,\n Question,\n type Recipient,\n Undo,\n Update,\n} from \"@fedify/vocab\";\nimport assert from \"node:assert\";\nimport { describe, test } from \"node:test\";\nimport { BotImpl } from \"./bot-impl.ts\";\nimport { createMessage } from \"./message-impl.ts\";\nimport { MemoryRepository, type Uuid } from \"./repository.ts\";\nimport { SessionImpl } from \"./session-impl.ts\";\nimport { mention, text } from \"./text.ts\";\n\ntest(\"SessionImpl.follow()\", async (t) => {\n const repository = new MemoryRepository();\n const bot = new BotImpl<void>({\n kv: new MemoryKvStore(),\n repository,\n username: \"bot\",\n });\n const ctx = createMockContext(bot, \"https://example.com\");\n const session = new SessionImpl(bot, ctx);\n\n await t.test(\"follow\", async () => {\n const actor = new Person({\n id: new URL(\"https://example.com/ap/actor/john\"),\n preferredUsername: \"john\",\n });\n await session.follow(actor);\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, [actor]);\n assert.ok(activity instanceof Follow);\n const parsed = ctx.parseUri(activity.id);\n assert.deepStrictEqual(parsed?.type, \"object\");\n assert.ok(parsed?.type === \"object\");\n assert.deepStrictEqual(parsed.class, Follow);\n assert.deepStrictEqual(activity.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(activity.objectId, actor.id);\n assert.deepStrictEqual(activity.toIds, [actor.id]);\n const follow = await repository.getSentFollow(parsed.values.id as Uuid);\n assert.ok(follow != null);\n assert.deepStrictEqual(\n await follow.toJsonLd({ format: \"compact\" }),\n await activity.toJsonLd({ format: \"compact\" }),\n );\n });\n\n await t.test(\"follow again\", async () => {\n ctx.sentActivities = [];\n await repository.addFollowee(\n new URL(\"https://example.com/ap/actor/alice\"),\n new Follow({\n id: new URL(\n \"https://example.com/ap/follow/4114eadb-2596-408f-ad99-06f467c9ace0\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n object: new URL(\"https://example.com/ap/actor/alice\"),\n }),\n );\n const actor = new Person({\n id: new URL(\"https://example.com/ap/actor/alice\"),\n preferredUsername: \"alice\",\n });\n await session.follow(actor);\n assert.deepStrictEqual(ctx.sentActivities, []);\n });\n\n await t.test(\"follow bot itself\", async () => {\n ctx.sentActivities = [];\n await assert.rejects(\n () => session.follow(session.actorId.href),\n TypeError,\n \"The bot cannot follow itself.\",\n );\n assert.deepStrictEqual(ctx.sentActivities, []);\n\n await assert.rejects(\n () => session.follow(session.actorId),\n TypeError,\n \"The bot cannot follow itself.\",\n );\n assert.deepStrictEqual(ctx.sentActivities, []);\n\n await assert.rejects(\n () => session.follow(session.actorHandle),\n TypeError,\n \"The bot cannot follow itself.\",\n );\n assert.deepStrictEqual(ctx.sentActivities, []);\n\n const actor = await session.getActor();\n await assert.rejects(\n () => session.follow(actor),\n TypeError,\n \"The bot cannot follow itself.\",\n );\n assert.deepStrictEqual(ctx.sentActivities, []);\n });\n});\n\ntest(\"SessionImpl.unfollow()\", async (t) => {\n const repository = new MemoryRepository();\n const bot = new BotImpl<void>({\n kv: new MemoryKvStore(),\n repository,\n username: \"bot\",\n });\n const ctx = createMockContext(bot, \"https://example.com\");\n const session = new SessionImpl(bot, ctx);\n\n await t.test(\"unfollow\", async () => {\n await repository.addFollowee(\n new URL(\"https://example.com/ap/actor/alice\"),\n new Follow({\n id: new URL(\n \"https://example.com/ap/follow/4114eadb-2596-408f-ad99-06f467c9ace0\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n object: new URL(\"https://example.com/ap/actor/alice\"),\n }),\n );\n const actor = new Person({\n id: new URL(\"https://example.com/ap/actor/alice\"),\n preferredUsername: \"alice\",\n });\n await session.unfollow(actor);\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, [actor]);\n assert.ok(activity instanceof Undo);\n const object = await activity.getObject(ctx);\n assert.ok(object instanceof Follow);\n assert.deepStrictEqual(\n object.id,\n new URL(\n \"https://example.com/ap/follow/4114eadb-2596-408f-ad99-06f467c9ace0\",\n ),\n );\n assert.deepStrictEqual(object.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(\n await repository.getFollowee(\n new URL(\"https://example.com/ap/actor/alice\"),\n ),\n undefined,\n );\n });\n\n await t.test(\"unfollow again\", async () => {\n ctx.sentActivities = [];\n const actor = new Person({\n id: new URL(\"https://example.com/ap/actor/alice\"),\n preferredUsername: \"alice\",\n });\n await session.unfollow(actor);\n assert.deepStrictEqual(ctx.sentActivities, []);\n });\n\n await t.test(\"unfollow bot itself\", async () => {\n ctx.sentActivities = [];\n await assert.rejects(\n () => session.unfollow(session.actorId.href),\n TypeError,\n \"The bot cannot unfollow itself.\",\n );\n assert.deepStrictEqual(ctx.sentActivities, []);\n\n await assert.rejects(\n () => session.unfollow(session.actorId),\n TypeError,\n \"The bot cannot unfollow itself.\",\n );\n assert.deepStrictEqual(ctx.sentActivities, []);\n\n await assert.rejects(\n () => session.unfollow(session.actorHandle),\n TypeError,\n \"The bot cannot unfollow itself.\",\n );\n assert.deepStrictEqual(ctx.sentActivities, []);\n\n const actor = await session.getActor();\n await assert.rejects(\n () => session.unfollow(actor),\n TypeError,\n \"The bot cannot unfollow itself.\",\n );\n assert.deepStrictEqual(ctx.sentActivities, []);\n });\n});\n\ndescribe(\"SessionImpl.follows()\", () => {\n const repository = new MemoryRepository();\n const bot = new BotImpl<void>({\n kv: new MemoryKvStore(),\n repository,\n username: \"bot\",\n });\n const ctx = createMockContext(bot, \"https://example.com\");\n const session = new SessionImpl(bot, ctx);\n\n test(\"when it follows\", async () => {\n const followeeId = new URL(\"https://example.com/ap/actor/alice\");\n const followee = new Person({\n id: followeeId,\n preferredUsername: \"alice\",\n });\n await repository.addFollowee(\n new URL(\"https://example.com/ap/actor/alice\"),\n new Follow({\n id: new URL(\n \"https://example.com/ap/follow/4114eadb-2596-408f-ad99-06f467c9ace0\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n object: followee,\n }),\n );\n assert.ok(await session.follows(followeeId.href));\n assert.ok(await session.follows(followeeId));\n assert.ok(await session.follows(followee));\n });\n\n test(\"when it does not follow\", async () => {\n const actorId = new URL(\"https://example.com/ap/actor/john\");\n const actor = new Person({\n id: actorId,\n preferredUsername: \"john\",\n });\n assert.deepStrictEqual(await session.follows(actorId.href), false);\n assert.deepStrictEqual(await session.follows(actorId), false);\n assert.deepStrictEqual(await session.follows(actor), false);\n });\n\n test(\"bot itself\", async () => {\n assert.deepStrictEqual(await session.follows(session.actorId.href), false);\n assert.deepStrictEqual(await session.follows(session.actorId), false);\n assert.deepStrictEqual(\n await session.follows(await session.getActor()),\n false,\n );\n assert.deepStrictEqual(await session.follows(session.actorHandle), false);\n });\n});\n\ntest(\"SessionImpl.republishProfile()\", async () => {\n const bot = new BotImpl<void>({\n kv: new MemoryKvStore(),\n username: \"bot\",\n name: \"Bot display name\",\n summary: text`This is the bot profile.`,\n icon: new URL(\"https://example.com/icon.png\"),\n image: new URL(\"https://example.com/header.png\"),\n });\n const ctx = createMockContext(bot, \"https://example.com\");\n const session = new SessionImpl(bot, ctx);\n\n await session.republishProfile();\n\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, \"followers\");\n assert.ok(activity instanceof Update);\n assert.deepStrictEqual(activity.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(activity.toIds, [ctx.getFollowersUri(bot.identifier)]);\n assert.deepStrictEqual(activity.ccIds, []);\n const actor = await activity.getObject(ctx);\n assert.ok(actor instanceof bot.class);\n assert.deepStrictEqual(actor.id, session.actorId);\n assert.deepStrictEqual(actor.name?.toString(), \"Bot display name\");\n assert.deepStrictEqual(\n actor.summary?.toString(),\n \"<p>This is the bot profile.</p>\",\n );\n const icon = await actor.getIcon();\n assert.ok(icon != null);\n assert.deepStrictEqual(icon.url, new URL(\"https://example.com/icon.png\"));\n const image = await actor.getImage();\n assert.ok(image != null);\n assert.deepStrictEqual(image.url, new URL(\"https://example.com/header.png\"));\n});\n\ntest(\"SessionImpl.publish()\", async (t) => {\n const kv = new MemoryKvStore();\n const bot = new BotImpl<void>({ kv, username: \"bot\" });\n const ctx = createMockContext(bot, \"https://example.com\");\n const session = new SessionImpl(bot, ctx);\n\n await t.test(\"public\", async () => {\n ctx.sentActivities = [];\n const publicMsg = await session.publish(text`Hello, world!`);\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, \"followers\");\n assert.ok(activity instanceof Create);\n assert.deepStrictEqual(activity.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(activity.toIds, [PUBLIC_COLLECTION]);\n assert.deepStrictEqual(activity.ccIds, [\n ctx.getFollowersUri(bot.identifier),\n ]);\n const object = await activity.getObject(ctx);\n assert.ok(object instanceof Note);\n assert.deepStrictEqual(\n object.attributionId,\n ctx.getActorUri(bot.identifier),\n );\n assert.deepStrictEqual(object.toIds, [PUBLIC_COLLECTION]);\n assert.deepStrictEqual(object.ccIds, [ctx.getFollowersUri(bot.identifier)]);\n assert.deepStrictEqual(object.content, \"<p>Hello, world!</p>\");\n assert.deepStrictEqual(object.tagIds, []);\n assert.deepStrictEqual(publicMsg.id, object.id);\n assert.deepStrictEqual(publicMsg.text, \"Hello, world!\");\n assert.deepStrictEqual(publicMsg.html, \"<p>Hello, world!</p>\");\n assert.deepStrictEqual(publicMsg.visibility, \"public\");\n assert.deepStrictEqual(publicMsg.mentions, []);\n });\n\n await t.test(\"unlisted\", async () => {\n ctx.sentActivities = [];\n const unlistedMsg = await session.publish(text`Hello!`, {\n visibility: \"unlisted\",\n });\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, \"followers\");\n assert.ok(activity instanceof Create);\n assert.deepStrictEqual(activity.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(activity.toIds, [\n ctx.getFollowersUri(bot.identifier),\n ]);\n assert.deepStrictEqual(activity.ccIds, [PUBLIC_COLLECTION]);\n const object = await activity.getObject(ctx);\n assert.ok(object instanceof Note);\n assert.deepStrictEqual(\n object.attributionId,\n ctx.getActorUri(bot.identifier),\n );\n assert.deepStrictEqual(object.toIds, [ctx.getFollowersUri(bot.identifier)]);\n assert.deepStrictEqual(object.ccIds, [PUBLIC_COLLECTION]);\n assert.deepStrictEqual(object.content, \"<p>Hello!</p>\");\n assert.deepStrictEqual(object.tagIds, []);\n assert.deepStrictEqual(unlistedMsg.id, object.id);\n assert.deepStrictEqual(unlistedMsg.text, \"Hello!\");\n assert.deepStrictEqual(unlistedMsg.html, \"<p>Hello!</p>\");\n assert.deepStrictEqual(unlistedMsg.visibility, \"unlisted\");\n assert.deepStrictEqual(unlistedMsg.mentions, []);\n });\n\n await t.test(\"followers\", async () => {\n ctx.sentActivities = [];\n const followersMsg = await session.publish(text`Hi!`, {\n visibility: \"followers\",\n });\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, \"followers\");\n assert.ok(activity instanceof Create);\n assert.deepStrictEqual(activity.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(activity.toIds, [\n ctx.getFollowersUri(bot.identifier),\n ]);\n assert.deepStrictEqual(activity.ccIds, []);\n const object = await activity.getObject(ctx);\n assert.ok(object instanceof Note);\n assert.deepStrictEqual(\n object.attributionId,\n ctx.getActorUri(bot.identifier),\n );\n assert.deepStrictEqual(object.toIds, [ctx.getFollowersUri(bot.identifier)]);\n assert.deepStrictEqual(object.ccIds, []);\n assert.deepStrictEqual(object.content, \"<p>Hi!</p>\");\n assert.deepStrictEqual(object.tagIds, []);\n assert.deepStrictEqual(followersMsg.id, object.id);\n assert.deepStrictEqual(followersMsg.text, \"Hi!\");\n assert.deepStrictEqual(followersMsg.html, \"<p>Hi!</p>\");\n assert.deepStrictEqual(followersMsg.visibility, \"followers\");\n assert.deepStrictEqual(followersMsg.mentions, []);\n });\n\n await t.test(\"direct\", async () => {\n const mentioned = new Person({\n id: new URL(\"https://example.com/ap/actor/john\"),\n preferredUsername: \"john\",\n });\n ctx.sentActivities = [];\n const directMsg = await session.publish(\n text`Hey ${mention(mentioned)}!`,\n { visibility: \"direct\" },\n );\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, [mentioned]);\n assert.ok(activity instanceof Create);\n assert.deepStrictEqual(activity.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(activity.toIds, [mentioned.id]);\n assert.deepStrictEqual(activity.ccIds, []);\n const object = await activity.getObject(ctx);\n assert.ok(object instanceof Note);\n assert.deepStrictEqual(\n object.attributionId,\n ctx.getActorUri(bot.identifier),\n );\n assert.deepStrictEqual(object.toIds, [mentioned.id]);\n assert.deepStrictEqual(object.ccIds, []);\n assert.deepStrictEqual(\n object.content,\n '<p>Hey <a href=\"https://example.com/ap/actor/john\" translate=\"no\" ' +\n 'class=\"h-card u-url mention\" target=\"_blank\">@<span>john@example.com' +\n \"</span></a>!</p>\",\n );\n const tags = await Array.fromAsync(object.getTags());\n assert.deepStrictEqual(tags.length, 1);\n assert.deepStrictEqual(directMsg.id, object.id);\n assert.deepStrictEqual(directMsg.text, \"Hey @john@example.com!\");\n assert.deepStrictEqual(directMsg.html, object.content);\n assert.deepStrictEqual(directMsg.visibility, \"direct\");\n // assert.deepStrictEqual(directMsg.mentions, [mentioned]); // FIXME\n });\n\n await t.test(\"quote\", async () => {\n const originalAuthor = new Person({\n id: new URL(\"https://example.com/ap/actor/john\"),\n preferredUsername: \"john\",\n });\n const originalPost = new Note({\n id: new URL(\n \"https://example.com/ap/note/c1c792ce-a0be-4685-b396-e59e5ef8c788\",\n ),\n content: \"<p>Hello, world!</p>\",\n attribution: originalAuthor,\n to: new URL(\"https://example.com/ap/actor/john/followers\"),\n cc: PUBLIC_COLLECTION,\n });\n const originalMsg = await createMessage<Note, void>(\n originalPost,\n session,\n {},\n );\n ctx.sentActivities = [];\n const quote = await session.publish(text`Check this out!`, {\n quoteTarget: originalMsg,\n });\n assert.deepStrictEqual(ctx.sentActivities.length, 2);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, \"followers\");\n assert.ok(activity instanceof Create);\n assert.deepStrictEqual(activity.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(activity.toIds, [PUBLIC_COLLECTION]);\n assert.deepStrictEqual(activity.ccIds, [\n ctx.getFollowersUri(bot.identifier),\n ]);\n const object = await activity.getObject(ctx);\n const { recipients: recipients2, activity: activity2 } =\n ctx.sentActivities[1];\n assert.deepStrictEqual(recipients2, [originalAuthor]);\n assert.ok(activity2 instanceof Create);\n assert.deepStrictEqual(activity2.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(activity2.toIds, [PUBLIC_COLLECTION]);\n assert.deepStrictEqual(activity2.ccIds, [\n ctx.getFollowersUri(bot.identifier),\n ]);\n assert.ok(object instanceof Note);\n assert.deepStrictEqual(\n object.attributionId,\n ctx.getActorUri(bot.identifier),\n );\n assert.deepStrictEqual(object.toIds, [PUBLIC_COLLECTION]);\n assert.deepStrictEqual(object.ccIds, [ctx.getFollowersUri(bot.identifier)]);\n assert.deepStrictEqual(\n object.content,\n `<p>Check this out!</p>\n\n<p class=\"quote-inline\"><br>RE: <a href=\"${originalMsg.id.href}\">${originalMsg.id.href}</a></p>`,\n );\n assert.deepStrictEqual(object.quoteUrl, originalMsg.id);\n assert.deepStrictEqual(quote.id, object.id);\n assert.deepStrictEqual(\n quote.text,\n `Check this out!\\n\\nRE: ${originalMsg.id.href}`,\n );\n assert.deepStrictEqual(\n quote.html,\n `<p>Check this out!</p>\n\n<p><br>RE: <a href=\"${originalMsg.id.href}\">${originalMsg.id.href}</a></p>`,\n );\n assert.deepStrictEqual(quote.visibility, \"public\");\n assert.deepStrictEqual(quote.quoteTarget?.id, originalMsg.id);\n });\n\n await t.test(\"poll single choice\", async () => {\n ctx.sentActivities = [];\n const endTime = Temporal.Now.instant().add({ hours: 24 });\n const poll = await session.publish(text`What's your favorite color?`, {\n class: Question,\n poll: {\n multiple: false,\n options: [\"Red\", \"Blue\", \"Green\"],\n endTime,\n },\n });\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, \"followers\");\n assert.ok(activity instanceof Create);\n assert.deepStrictEqual(activity.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(activity.toIds, [PUBLIC_COLLECTION]);\n assert.deepStrictEqual(activity.ccIds, [\n ctx.getFollowersUri(bot.identifier),\n ]);\n const object = await activity.getObject(ctx);\n assert.ok(object instanceof Question);\n assert.deepStrictEqual(\n object.attributionId,\n ctx.getActorUri(bot.identifier),\n );\n assert.deepStrictEqual(object.toIds, [PUBLIC_COLLECTION]);\n assert.deepStrictEqual(object.ccIds, [ctx.getFollowersUri(bot.identifier)]);\n assert.deepStrictEqual(\n object.content,\n \"<p>What's your favorite color?</p>\",\n );\n assert.deepStrictEqual(object.endTime, endTime);\n assert.deepStrictEqual(object.voters, 0);\n assert.deepStrictEqual(object.inclusiveOptionIds, []);\n\n const exclusiveOptions = await Array.fromAsync(\n object.getExclusiveOptions(ctx),\n );\n assert.deepStrictEqual(exclusiveOptions.length, 3);\n assert.ok(exclusiveOptions[0] instanceof Note);\n assert.deepStrictEqual(exclusiveOptions[0].name?.toString(), \"Red\");\n assert.ok(exclusiveOptions[1] instanceof Note);\n assert.deepStrictEqual(exclusiveOptions[1].name?.toString(), \"Blue\");\n assert.ok(exclusiveOptions[2] instanceof Note);\n assert.deepStrictEqual(exclusiveOptions[2].name?.toString(), \"Green\");\n\n for (const option of exclusiveOptions) {\n const replies = await option.getReplies(ctx);\n assert.deepStrictEqual(replies?.totalItems, 0);\n }\n\n assert.deepStrictEqual(poll.id, object.id);\n assert.deepStrictEqual(poll.text, \"What's your favorite color?\");\n assert.deepStrictEqual(\n poll.html,\n \"<p>What's your favorite color?</p>\",\n );\n assert.deepStrictEqual(poll.visibility, \"public\");\n });\n\n await t.test(\"poll multiple choice\", async () => {\n ctx.sentActivities = [];\n const endTime = Temporal.Now.instant().add({ hours: 24 * 7 });\n const poll = await session.publish(\n text`Which programming languages do you know?`,\n {\n class: Question,\n poll: {\n multiple: true,\n options: [\"JavaScript\", \"TypeScript\", \"Python\", \"Rust\"],\n endTime,\n },\n visibility: \"unlisted\",\n },\n );\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, \"followers\");\n assert.ok(activity instanceof Create);\n const object = await activity.getObject(ctx);\n assert.ok(object instanceof Question);\n assert.deepStrictEqual(object.endTime, endTime);\n assert.deepStrictEqual(object.voters, 0);\n assert.deepStrictEqual(object.exclusiveOptionIds, []);\n\n const inclusiveOptions = await Array.fromAsync(\n object.getInclusiveOptions(ctx),\n );\n assert.deepStrictEqual(inclusiveOptions.length, 4);\n assert.ok(inclusiveOptions[0] instanceof Note);\n assert.deepStrictEqual(inclusiveOptions[0].name?.toString(), \"JavaScript\");\n assert.ok(inclusiveOptions[1] instanceof Note);\n assert.deepStrictEqual(inclusiveOptions[1].name?.toString(), \"TypeScript\");\n assert.ok(inclusiveOptions[2] instanceof Note);\n assert.deepStrictEqual(inclusiveOptions[2].name?.toString(), \"Python\");\n assert.ok(inclusiveOptions[3] instanceof Note);\n assert.deepStrictEqual(inclusiveOptions[3].name?.toString(), \"Rust\");\n\n assert.deepStrictEqual(poll.visibility, \"unlisted\");\n assert.deepStrictEqual(activity.toIds, [\n ctx.getFollowersUri(bot.identifier),\n ]);\n assert.deepStrictEqual(activity.ccIds, [PUBLIC_COLLECTION]);\n });\n\n await t.test(\"poll with direct visibility\", async () => {\n const mentioned = new Person({\n id: new URL(\"https://example.com/ap/actor/alice\"),\n preferredUsername: \"alice\",\n });\n ctx.sentActivities = [];\n const endTime = Temporal.Now.instant().add({ hours: 12 });\n const poll = await session.publish(\n text`Hey ${mention(mentioned)}, what do you think?`,\n {\n class: Question,\n poll: {\n multiple: false,\n options: [\"Good\", \"Bad\", \"Neutral\"],\n endTime,\n },\n visibility: \"direct\",\n },\n );\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, [mentioned]);\n assert.ok(activity instanceof Create);\n const object = await activity.getObject(ctx);\n assert.ok(object instanceof Question);\n assert.deepStrictEqual(object.toIds, [mentioned.id]);\n assert.deepStrictEqual(object.ccIds, []);\n assert.deepStrictEqual(poll.visibility, \"direct\");\n });\n\n await t.test(\"poll end-to-end workflow\", async () => {\n // Create fresh repository and session for isolation\n const freshRepository = new MemoryRepository();\n const freshBot = new BotImpl<void>({\n kv: new MemoryKvStore(),\n repository: freshRepository,\n username: \"testbot\",\n });\n const freshCtx = createMockContext(freshBot, \"https://example.com\");\n const freshSession = new SessionImpl(freshBot, freshCtx);\n\n const endTime = Temporal.Now.instant().add({ hours: 1 });\n\n // 1. Create a poll\n const poll = await freshSession.publish(\n text`What should we have for lunch?`,\n {\n class: Question,\n poll: {\n multiple: false,\n options: [\"Pizza\", \"Burgers\", \"Salad\"],\n endTime,\n },\n },\n );\n\n // Verify poll was created correctly\n assert.deepStrictEqual(freshCtx.sentActivities.length, 1);\n const { activity: createActivity } = freshCtx.sentActivities[0];\n assert.ok(createActivity instanceof Create);\n const pollObject = await createActivity.getObject(freshCtx);\n assert.ok(pollObject instanceof Question);\n assert.deepStrictEqual(pollObject.endTime, endTime);\n\n // Get poll options\n const options = await Array.fromAsync(\n pollObject.getExclusiveOptions(freshCtx),\n );\n assert.deepStrictEqual(options.length, 3);\n assert.deepStrictEqual(options[0].name?.toString(), \"Pizza\");\n assert.deepStrictEqual(options[1].name?.toString(), \"Burgers\");\n assert.deepStrictEqual(options[2].name?.toString(), \"Salad\");\n\n // 2. Verify poll is accessible via getOutbox\n const outbox = freshSession.getOutbox({ order: \"newest\" });\n const messages = await Array.fromAsync(outbox);\n assert.deepStrictEqual(messages.length, 1);\n assert.deepStrictEqual(messages[0].id, poll.id);\n assert.deepStrictEqual(messages[0].text, \"What should we have for lunch?\");\n\n // 3. Verify poll structure\n assert.deepStrictEqual(poll.visibility, \"public\");\n assert.deepStrictEqual(poll.mentions, []);\n });\n});\n\ntest(\"SessionImpl.getOutbox()\", async (t) => {\n const repository = new MemoryRepository();\n const bot = new BotImpl<void>({\n kv: new MemoryKvStore(),\n repository,\n username: \"bot\",\n });\n const ctx = createMockContext(bot, \"https://example.com\");\n const session = new SessionImpl(bot, ctx);\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 await repository.addMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\", messageA);\n await repository.addMessage(\"0194244f-d800-7873-8993-ef71ccd47306\", messageB);\n await repository.addMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\", messageC);\n await repository.addMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\", messageD);\n\n await t.test(\"default\", async () => {\n const outbox = session.getOutbox({ order: \"oldest\" });\n const messages = await Array.fromAsync(outbox);\n assert.deepStrictEqual(messages.length, 4);\n\n assert.deepStrictEqual(\n messages[0].id.href,\n \"https://example.com/ap/note/01941f29-7c00-7fe8-ab0a-7b593990a3c0\",\n );\n assert.deepStrictEqual(\n messages[0].actor.id?.href,\n \"https://example.com/ap/actor/bot\",\n );\n assert.deepStrictEqual(messages[0].visibility, \"unlisted\");\n assert.deepStrictEqual(messages[0].text, \"Hello, world!\");\n assert.deepStrictEqual(\n messages[0].published,\n Temporal.Instant.from(\"2025-01-01T00:00:00Z\"),\n );\n\n assert.deepStrictEqual(\n messages[1].id.href,\n \"https://example.com/ap/note/0194244f-d800-7873-8993-ef71ccd47306\",\n );\n assert.deepStrictEqual(\n messages[1].actor.id?.href,\n \"https://example.com/ap/actor/bot\",\n );\n assert.deepStrictEqual(messages[1].visibility, \"unlisted\");\n assert.deepStrictEqual(messages[1].text, \"Hello, world!\");\n assert.deepStrictEqual(\n messages[1].published,\n Temporal.Instant.from(\"2025-01-02T00:00:00Z\"),\n );\n\n assert.deepStrictEqual(\n messages[2].id.href,\n \"https://example.com/ap/note/01942976-3400-7f34-872e-2cbf0f9eeac4\",\n );\n assert.deepStrictEqual(\n messages[2].actor.id?.href,\n \"https://example.com/ap/actor/bot\",\n );\n assert.deepStrictEqual(messages[2].visibility, \"unlisted\");\n assert.deepStrictEqual(messages[2].text, \"Hello, world!\");\n assert.deepStrictEqual(\n messages[2].published,\n Temporal.Instant.from(\"2025-01-03T00:00:00Z\"),\n );\n\n assert.deepStrictEqual(\n messages[3].id.href,\n \"https://example.com/ap/note/01942e9c-9000-7480-a553-7a6ce737ce14\",\n );\n assert.deepStrictEqual(\n messages[3].actor.id?.href,\n \"https://example.com/ap/actor/bot\",\n );\n assert.deepStrictEqual(messages[3].visibility, \"unlisted\");\n assert.deepStrictEqual(messages[3].text, \"Hello, world!\");\n assert.deepStrictEqual(\n messages[3].published,\n Temporal.Instant.from(\"2025-01-04T00:00:00Z\"),\n );\n });\n\n await t.test(\"order: 'oldest'\", async () => {\n const outbox = session.getOutbox({ order: \"oldest\" });\n const messages = await Array.fromAsync(outbox);\n const messageIds = messages.map((msg) => msg.id.href);\n assert.deepStrictEqual(messageIds, [\n \"https://example.com/ap/note/01941f29-7c00-7fe8-ab0a-7b593990a3c0\",\n \"https://example.com/ap/note/0194244f-d800-7873-8993-ef71ccd47306\",\n \"https://example.com/ap/note/01942976-3400-7f34-872e-2cbf0f9eeac4\",\n \"https://example.com/ap/note/01942e9c-9000-7480-a553-7a6ce737ce14\",\n ]);\n });\n\n await t.test(\"order: 'newest'\", async () => {\n const outbox = session.getOutbox({ order: \"newest\" });\n const messages = await Array.fromAsync(outbox);\n const messageIds = messages.map((msg) => msg.id.href);\n assert.deepStrictEqual(messageIds, [\n \"https://example.com/ap/note/01942e9c-9000-7480-a553-7a6ce737ce14\",\n \"https://example.com/ap/note/01942976-3400-7f34-872e-2cbf0f9eeac4\",\n \"https://example.com/ap/note/0194244f-d800-7873-8993-ef71ccd47306\",\n \"https://example.com/ap/note/01941f29-7c00-7fe8-ab0a-7b593990a3c0\",\n ]);\n });\n\n await t.test(\"since\", async () => {\n const outbox = session.getOutbox({\n since: Temporal.Instant.from(\"2025-01-03T00:00:00Z\"),\n });\n const messages = await Array.fromAsync(outbox);\n const messageIds = messages.map((msg) => msg.id.href);\n assert.deepStrictEqual(messageIds, [\n \"https://example.com/ap/note/01942e9c-9000-7480-a553-7a6ce737ce14\",\n \"https://example.com/ap/note/01942976-3400-7f34-872e-2cbf0f9eeac4\",\n ]);\n });\n\n await t.test(\"until\", async () => {\n const outbox = session.getOutbox({\n until: Temporal.Instant.from(\"2025-01-02T00:00:00Z\"),\n });\n const messages = await Array.fromAsync(outbox);\n const messageIds = messages.map((msg) => msg.id.href);\n assert.deepStrictEqual(messageIds, [\n \"https://example.com/ap/note/0194244f-d800-7873-8993-ef71ccd47306\",\n \"https://example.com/ap/note/01941f29-7c00-7fe8-ab0a-7b593990a3c0\",\n ]);\n });\n\n await t.test(\"messages should have update and delete methods\", async () => {\n const outbox = session.getOutbox({ order: \"oldest\" });\n const messages = await Array.fromAsync(outbox);\n assert.strictEqual(messages.length, 4);\n\n for (const message of messages) {\n assert.strictEqual(typeof message.update, \"function\");\n assert.strictEqual(typeof message.delete, \"function\");\n }\n });\n});\n\nexport interface SentActivity {\n recipients: \"followers\" | Recipient[];\n activity: Activity;\n}\n\nexport interface MockContext extends Context<void> {\n sentActivities: SentActivity[];\n}\n\nexport function createMockContext(\n bot: BotImpl<void>,\n origin: URL | string,\n): MockContext {\n const ctx = bot.federation.createContext(\n new URL(origin),\n undefined,\n ) as MockContext;\n ctx.sentActivities = [];\n ctx.sendActivity = (_, recipients, activity) => {\n ctx.sentActivities.push({\n recipients: recipients === \"followers\"\n ? \"followers\"\n : Array.isArray(recipients)\n ? recipients\n : [recipients],\n activity,\n });\n return Promise.resolve();\n };\n return ctx;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"session-impl.test.js","names":["bot: BotImpl<void>","origin: URL | string"],"sources":["../src/session-impl.test.ts"],"sourcesContent":["// BotKit by Fedify: A framework for creating ActivityPub bots\n// Copyright (C) 2025–2026 Hong Minhee <https://hongminhee.org/>\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\nimport { type Context, MemoryKvStore } from \"@fedify/fedify/federation\";\nimport {\n type Activity,\n Create,\n Follow,\n Note,\n Person,\n PUBLIC_COLLECTION,\n Question,\n type Recipient,\n Undo,\n Update,\n} from \"@fedify/vocab\";\nimport assert from \"node:assert\";\nimport { describe, test } from \"node:test\";\nimport { BotImpl } from \"./bot-impl.ts\";\nimport { createMessage } from \"./message-impl.ts\";\nimport { MemoryRepository, type Uuid } from \"./repository.ts\";\nimport { SessionImpl } from \"./session-impl.ts\";\nimport { mention, text } from \"./text.ts\";\n\ntest(\"SessionImpl.follow()\", async (t) => {\n const repository = new MemoryRepository();\n const bot = new BotImpl<void>({\n kv: new MemoryKvStore(),\n repository,\n username: \"bot\",\n });\n const ctx = createMockContext(bot, \"https://example.com\");\n const session = new SessionImpl(bot, ctx);\n\n await t.test(\"follow\", async () => {\n const actor = new Person({\n id: new URL(\"https://example.com/ap/actor/john\"),\n preferredUsername: \"john\",\n });\n await session.follow(actor);\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, [actor]);\n assert.ok(activity instanceof Follow);\n const parsed = ctx.parseUri(activity.id);\n assert.deepStrictEqual(parsed?.type, \"object\");\n assert.ok(parsed?.type === \"object\");\n assert.deepStrictEqual(parsed.class, Follow);\n assert.deepStrictEqual(activity.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(activity.objectId, actor.id);\n assert.deepStrictEqual(activity.toIds, [actor.id]);\n const follow = await repository.getSentFollow(parsed.values.id as Uuid);\n assert.ok(follow != null);\n assert.deepStrictEqual(\n await follow.toJsonLd({ format: \"compact\" }),\n await activity.toJsonLd({ format: \"compact\" }),\n );\n });\n\n await t.test(\"follow again\", async () => {\n ctx.sentActivities = [];\n await repository.addFollowee(\n new URL(\"https://example.com/ap/actor/alice\"),\n new Follow({\n id: new URL(\n \"https://example.com/ap/follow/4114eadb-2596-408f-ad99-06f467c9ace0\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n object: new URL(\"https://example.com/ap/actor/alice\"),\n }),\n );\n const actor = new Person({\n id: new URL(\"https://example.com/ap/actor/alice\"),\n preferredUsername: \"alice\",\n });\n await session.follow(actor);\n assert.deepStrictEqual(ctx.sentActivities, []);\n });\n\n await t.test(\"follow bot itself\", async () => {\n ctx.sentActivities = [];\n await assert.rejects(\n () => session.follow(session.actorId.href),\n TypeError,\n \"The bot cannot follow itself.\",\n );\n assert.deepStrictEqual(ctx.sentActivities, []);\n\n await assert.rejects(\n () => session.follow(session.actorId),\n TypeError,\n \"The bot cannot follow itself.\",\n );\n assert.deepStrictEqual(ctx.sentActivities, []);\n\n await assert.rejects(\n () => session.follow(session.actorHandle),\n TypeError,\n \"The bot cannot follow itself.\",\n );\n assert.deepStrictEqual(ctx.sentActivities, []);\n\n const actor = await session.getActor();\n await assert.rejects(\n () => session.follow(actor),\n TypeError,\n \"The bot cannot follow itself.\",\n );\n assert.deepStrictEqual(ctx.sentActivities, []);\n });\n});\n\ntest(\"SessionImpl.unfollow()\", async (t) => {\n const repository = new MemoryRepository();\n const bot = new BotImpl<void>({\n kv: new MemoryKvStore(),\n repository,\n username: \"bot\",\n });\n const ctx = createMockContext(bot, \"https://example.com\");\n const session = new SessionImpl(bot, ctx);\n\n await t.test(\"unfollow\", async () => {\n await repository.addFollowee(\n new URL(\"https://example.com/ap/actor/alice\"),\n new Follow({\n id: new URL(\n \"https://example.com/ap/follow/4114eadb-2596-408f-ad99-06f467c9ace0\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n object: new URL(\"https://example.com/ap/actor/alice\"),\n }),\n );\n const actor = new Person({\n id: new URL(\"https://example.com/ap/actor/alice\"),\n preferredUsername: \"alice\",\n });\n await session.unfollow(actor);\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, [actor]);\n assert.ok(activity instanceof Undo);\n const object = await activity.getObject(ctx);\n assert.ok(object instanceof Follow);\n assert.deepStrictEqual(\n object.id,\n new URL(\n \"https://example.com/ap/follow/4114eadb-2596-408f-ad99-06f467c9ace0\",\n ),\n );\n assert.deepStrictEqual(object.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(\n await repository.getFollowee(\n new URL(\"https://example.com/ap/actor/alice\"),\n ),\n undefined,\n );\n });\n\n await t.test(\"unfollow again\", async () => {\n ctx.sentActivities = [];\n const actor = new Person({\n id: new URL(\"https://example.com/ap/actor/alice\"),\n preferredUsername: \"alice\",\n });\n await session.unfollow(actor);\n assert.deepStrictEqual(ctx.sentActivities, []);\n });\n\n await t.test(\"unfollow bot itself\", async () => {\n ctx.sentActivities = [];\n await assert.rejects(\n () => session.unfollow(session.actorId.href),\n TypeError,\n \"The bot cannot unfollow itself.\",\n );\n assert.deepStrictEqual(ctx.sentActivities, []);\n\n await assert.rejects(\n () => session.unfollow(session.actorId),\n TypeError,\n \"The bot cannot unfollow itself.\",\n );\n assert.deepStrictEqual(ctx.sentActivities, []);\n\n await assert.rejects(\n () => session.unfollow(session.actorHandle),\n TypeError,\n \"The bot cannot unfollow itself.\",\n );\n assert.deepStrictEqual(ctx.sentActivities, []);\n\n const actor = await session.getActor();\n await assert.rejects(\n () => session.unfollow(actor),\n TypeError,\n \"The bot cannot unfollow itself.\",\n );\n assert.deepStrictEqual(ctx.sentActivities, []);\n });\n});\n\ndescribe(\"SessionImpl.follows()\", () => {\n const repository = new MemoryRepository();\n const bot = new BotImpl<void>({\n kv: new MemoryKvStore(),\n repository,\n username: \"bot\",\n });\n const ctx = createMockContext(bot, \"https://example.com\");\n const session = new SessionImpl(bot, ctx);\n\n test(\"when it follows\", async () => {\n const followeeId = new URL(\"https://example.com/ap/actor/alice\");\n const followee = new Person({\n id: followeeId,\n preferredUsername: \"alice\",\n });\n await repository.addFollowee(\n new URL(\"https://example.com/ap/actor/alice\"),\n new Follow({\n id: new URL(\n \"https://example.com/ap/follow/4114eadb-2596-408f-ad99-06f467c9ace0\",\n ),\n actor: new URL(\"https://example.com/ap/actor/bot\"),\n object: followee,\n }),\n );\n assert.ok(await session.follows(followeeId.href));\n assert.ok(await session.follows(followeeId));\n assert.ok(await session.follows(followee));\n });\n\n test(\"when it does not follow\", async () => {\n const actorId = new URL(\"https://example.com/ap/actor/john\");\n const actor = new Person({\n id: actorId,\n preferredUsername: \"john\",\n });\n assert.deepStrictEqual(await session.follows(actorId.href), false);\n assert.deepStrictEqual(await session.follows(actorId), false);\n assert.deepStrictEqual(await session.follows(actor), false);\n });\n\n test(\"bot itself\", async () => {\n assert.deepStrictEqual(await session.follows(session.actorId.href), false);\n assert.deepStrictEqual(await session.follows(session.actorId), false);\n assert.deepStrictEqual(\n await session.follows(await session.getActor()),\n false,\n );\n assert.deepStrictEqual(await session.follows(session.actorHandle), false);\n });\n});\n\ntest(\"SessionImpl.republishProfile()\", async () => {\n const bot = new BotImpl<void>({\n kv: new MemoryKvStore(),\n username: \"bot\",\n name: \"Bot display name\",\n summary: text`This is the bot profile.`,\n icon: new URL(\"https://example.com/icon.png\"),\n image: new URL(\"https://example.com/header.png\"),\n });\n const ctx = createMockContext(bot, \"https://example.com\");\n const session = new SessionImpl(bot, ctx);\n\n await session.republishProfile();\n\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, \"followers\");\n assert.ok(activity instanceof Update);\n assert.deepStrictEqual(activity.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(activity.toIds, [ctx.getFollowersUri(bot.identifier)]);\n assert.deepStrictEqual(activity.ccIds, []);\n const actor = await activity.getObject(ctx);\n assert.ok(actor instanceof bot.class);\n assert.deepStrictEqual(actor.id, session.actorId);\n assert.deepStrictEqual(actor.name?.toString(), \"Bot display name\");\n assert.deepStrictEqual(\n actor.summary?.toString(),\n \"<p>This is the bot profile.</p>\",\n );\n const icon = await actor.getIcon();\n assert.ok(icon != null);\n assert.deepStrictEqual(icon.url, new URL(\"https://example.com/icon.png\"));\n const image = await actor.getImage();\n assert.ok(image != null);\n assert.deepStrictEqual(image.url, new URL(\"https://example.com/header.png\"));\n});\n\ntest(\"SessionImpl.publish()\", async (t) => {\n const kv = new MemoryKvStore();\n const bot = new BotImpl<void>({ kv, username: \"bot\" });\n const ctx = createMockContext(bot, \"https://example.com\");\n const session = new SessionImpl(bot, ctx);\n\n await t.test(\"public\", async () => {\n ctx.sentActivities = [];\n const publicMsg = await session.publish(text`Hello, world!`);\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, \"followers\");\n assert.ok(activity instanceof Create);\n assert.deepStrictEqual(activity.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(activity.toIds, [PUBLIC_COLLECTION]);\n assert.deepStrictEqual(activity.ccIds, [\n ctx.getFollowersUri(bot.identifier),\n ]);\n const object = await activity.getObject(ctx);\n assert.ok(object instanceof Note);\n assert.deepStrictEqual(\n object.attributionId,\n ctx.getActorUri(bot.identifier),\n );\n assert.deepStrictEqual(object.toIds, [PUBLIC_COLLECTION]);\n assert.deepStrictEqual(object.ccIds, [ctx.getFollowersUri(bot.identifier)]);\n assert.deepStrictEqual(object.content, \"<p>Hello, world!</p>\");\n assert.deepStrictEqual(object.tagIds, []);\n assert.deepStrictEqual(publicMsg.id, object.id);\n assert.deepStrictEqual(publicMsg.text, \"Hello, world!\");\n assert.deepStrictEqual(publicMsg.html, \"<p>Hello, world!</p>\");\n assert.deepStrictEqual(publicMsg.visibility, \"public\");\n assert.deepStrictEqual(publicMsg.mentions, []);\n });\n\n await t.test(\"unlisted\", async () => {\n ctx.sentActivities = [];\n const unlistedMsg = await session.publish(text`Hello!`, {\n visibility: \"unlisted\",\n });\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, \"followers\");\n assert.ok(activity instanceof Create);\n assert.deepStrictEqual(activity.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(activity.toIds, [\n ctx.getFollowersUri(bot.identifier),\n ]);\n assert.deepStrictEqual(activity.ccIds, [PUBLIC_COLLECTION]);\n const object = await activity.getObject(ctx);\n assert.ok(object instanceof Note);\n assert.deepStrictEqual(\n object.attributionId,\n ctx.getActorUri(bot.identifier),\n );\n assert.deepStrictEqual(object.toIds, [ctx.getFollowersUri(bot.identifier)]);\n assert.deepStrictEqual(object.ccIds, [PUBLIC_COLLECTION]);\n assert.deepStrictEqual(object.content, \"<p>Hello!</p>\");\n assert.deepStrictEqual(object.tagIds, []);\n assert.deepStrictEqual(unlistedMsg.id, object.id);\n assert.deepStrictEqual(unlistedMsg.text, \"Hello!\");\n assert.deepStrictEqual(unlistedMsg.html, \"<p>Hello!</p>\");\n assert.deepStrictEqual(unlistedMsg.visibility, \"unlisted\");\n assert.deepStrictEqual(unlistedMsg.mentions, []);\n });\n\n await t.test(\"followers\", async () => {\n ctx.sentActivities = [];\n const followersMsg = await session.publish(text`Hi!`, {\n visibility: \"followers\",\n });\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, \"followers\");\n assert.ok(activity instanceof Create);\n assert.deepStrictEqual(activity.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(activity.toIds, [\n ctx.getFollowersUri(bot.identifier),\n ]);\n assert.deepStrictEqual(activity.ccIds, []);\n const object = await activity.getObject(ctx);\n assert.ok(object instanceof Note);\n assert.deepStrictEqual(\n object.attributionId,\n ctx.getActorUri(bot.identifier),\n );\n assert.deepStrictEqual(object.toIds, [ctx.getFollowersUri(bot.identifier)]);\n assert.deepStrictEqual(object.ccIds, []);\n assert.deepStrictEqual(object.content, \"<p>Hi!</p>\");\n assert.deepStrictEqual(object.tagIds, []);\n assert.deepStrictEqual(followersMsg.id, object.id);\n assert.deepStrictEqual(followersMsg.text, \"Hi!\");\n assert.deepStrictEqual(followersMsg.html, \"<p>Hi!</p>\");\n assert.deepStrictEqual(followersMsg.visibility, \"followers\");\n assert.deepStrictEqual(followersMsg.mentions, []);\n });\n\n await t.test(\"direct\", async () => {\n const mentioned = new Person({\n id: new URL(\"https://example.com/ap/actor/john\"),\n preferredUsername: \"john\",\n });\n ctx.sentActivities = [];\n const directMsg = await session.publish(\n text`Hey ${mention(mentioned)}!`,\n { visibility: \"direct\" },\n );\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, [mentioned]);\n assert.ok(activity instanceof Create);\n assert.deepStrictEqual(activity.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(activity.toIds, [mentioned.id]);\n assert.deepStrictEqual(activity.ccIds, []);\n const object = await activity.getObject(ctx);\n assert.ok(object instanceof Note);\n assert.deepStrictEqual(\n object.attributionId,\n ctx.getActorUri(bot.identifier),\n );\n assert.deepStrictEqual(object.toIds, [mentioned.id]);\n assert.deepStrictEqual(object.ccIds, []);\n assert.deepStrictEqual(\n object.content,\n '<p>Hey <a href=\"https://example.com/ap/actor/john\" translate=\"no\" ' +\n 'class=\"h-card u-url mention\" target=\"_blank\">@<span>john@example.com' +\n \"</span></a>!</p>\",\n );\n const tags = await Array.fromAsync(object.getTags());\n assert.deepStrictEqual(tags.length, 1);\n assert.deepStrictEqual(directMsg.id, object.id);\n assert.deepStrictEqual(directMsg.text, \"Hey @john@example.com!\");\n assert.deepStrictEqual(directMsg.html, object.content);\n assert.deepStrictEqual(directMsg.visibility, \"direct\");\n // assert.deepStrictEqual(directMsg.mentions, [mentioned]); // FIXME\n });\n\n await t.test(\"quote\", async () => {\n const originalAuthor = new Person({\n id: new URL(\"https://example.com/ap/actor/john\"),\n preferredUsername: \"john\",\n });\n const originalPost = new Note({\n id: new URL(\n \"https://example.com/ap/note/c1c792ce-a0be-4685-b396-e59e5ef8c788\",\n ),\n content: \"<p>Hello, world!</p>\",\n attribution: originalAuthor,\n to: new URL(\"https://example.com/ap/actor/john/followers\"),\n cc: PUBLIC_COLLECTION,\n });\n const originalMsg = await createMessage<Note, void>(\n originalPost,\n session,\n {},\n );\n ctx.sentActivities = [];\n const quote = await session.publish(text`Check this out!`, {\n quoteTarget: originalMsg,\n });\n assert.deepStrictEqual(ctx.sentActivities.length, 2);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, \"followers\");\n assert.ok(activity instanceof Create);\n assert.deepStrictEqual(activity.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(activity.toIds, [PUBLIC_COLLECTION]);\n assert.deepStrictEqual(activity.ccIds, [\n ctx.getFollowersUri(bot.identifier),\n ]);\n const object = await activity.getObject(ctx);\n const { recipients: recipients2, activity: activity2 } =\n ctx.sentActivities[1];\n assert.deepStrictEqual(recipients2, [originalAuthor]);\n assert.ok(activity2 instanceof Create);\n assert.deepStrictEqual(activity2.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(activity2.toIds, [PUBLIC_COLLECTION]);\n assert.deepStrictEqual(activity2.ccIds, [\n ctx.getFollowersUri(bot.identifier),\n ]);\n assert.ok(object instanceof Note);\n assert.deepStrictEqual(\n object.attributionId,\n ctx.getActorUri(bot.identifier),\n );\n assert.deepStrictEqual(object.toIds, [PUBLIC_COLLECTION]);\n assert.deepStrictEqual(object.ccIds, [ctx.getFollowersUri(bot.identifier)]);\n assert.deepStrictEqual(\n object.content,\n `<p>Check this out!</p>\n\n<p class=\"quote-inline\"><br>RE: <a href=\"${originalMsg.id.href}\">${originalMsg.id.href}</a></p>`,\n );\n assert.deepStrictEqual(object.quoteUrl, originalMsg.id);\n assert.deepStrictEqual(quote.id, object.id);\n assert.deepStrictEqual(\n quote.text,\n `Check this out!\\n\\nRE: ${originalMsg.id.href}`,\n );\n assert.deepStrictEqual(\n quote.html,\n `<p>Check this out!</p>\n\n<p><br>RE: <a href=\"${originalMsg.id.href}\">${originalMsg.id.href}</a></p>`,\n );\n assert.deepStrictEqual(quote.visibility, \"public\");\n assert.deepStrictEqual(quote.quoteTarget?.id, originalMsg.id);\n });\n\n await t.test(\"poll single choice\", async () => {\n ctx.sentActivities = [];\n const endTime = Temporal.Now.instant().add({ hours: 24 });\n const poll = await session.publish(text`What's your favorite color?`, {\n class: Question,\n poll: {\n multiple: false,\n options: [\"Red\", \"Blue\", \"Green\"],\n endTime,\n },\n });\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, \"followers\");\n assert.ok(activity instanceof Create);\n assert.deepStrictEqual(activity.actorId, ctx.getActorUri(bot.identifier));\n assert.deepStrictEqual(activity.toIds, [PUBLIC_COLLECTION]);\n assert.deepStrictEqual(activity.ccIds, [\n ctx.getFollowersUri(bot.identifier),\n ]);\n const object = await activity.getObject(ctx);\n assert.ok(object instanceof Question);\n assert.deepStrictEqual(\n object.attributionId,\n ctx.getActorUri(bot.identifier),\n );\n assert.deepStrictEqual(object.toIds, [PUBLIC_COLLECTION]);\n assert.deepStrictEqual(object.ccIds, [ctx.getFollowersUri(bot.identifier)]);\n assert.deepStrictEqual(\n object.content,\n \"<p>What's your favorite color?</p>\",\n );\n assert.deepStrictEqual(object.endTime, endTime);\n assert.deepStrictEqual(object.voters, 0);\n assert.deepStrictEqual(object.inclusiveOptionIds, []);\n\n const exclusiveOptions = await Array.fromAsync(\n object.getExclusiveOptions(ctx),\n );\n assert.deepStrictEqual(exclusiveOptions.length, 3);\n assert.ok(exclusiveOptions[0] instanceof Note);\n assert.deepStrictEqual(exclusiveOptions[0].name?.toString(), \"Red\");\n assert.ok(exclusiveOptions[1] instanceof Note);\n assert.deepStrictEqual(exclusiveOptions[1].name?.toString(), \"Blue\");\n assert.ok(exclusiveOptions[2] instanceof Note);\n assert.deepStrictEqual(exclusiveOptions[2].name?.toString(), \"Green\");\n\n for (const option of exclusiveOptions) {\n const replies = await option.getReplies(ctx);\n assert.deepStrictEqual(replies?.totalItems, 0);\n }\n\n assert.deepStrictEqual(poll.id, object.id);\n assert.deepStrictEqual(poll.text, \"What's your favorite color?\");\n assert.deepStrictEqual(\n poll.html,\n \"<p>What's your favorite color?</p>\",\n );\n assert.deepStrictEqual(poll.visibility, \"public\");\n });\n\n await t.test(\"poll multiple choice\", async () => {\n ctx.sentActivities = [];\n const endTime = Temporal.Now.instant().add({ hours: 24 * 7 });\n const poll = await session.publish(\n text`Which programming languages do you know?`,\n {\n class: Question,\n poll: {\n multiple: true,\n options: [\"JavaScript\", \"TypeScript\", \"Python\", \"Rust\"],\n endTime,\n },\n visibility: \"unlisted\",\n },\n );\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, \"followers\");\n assert.ok(activity instanceof Create);\n const object = await activity.getObject(ctx);\n assert.ok(object instanceof Question);\n assert.deepStrictEqual(object.endTime, endTime);\n assert.deepStrictEqual(object.voters, 0);\n assert.deepStrictEqual(object.exclusiveOptionIds, []);\n\n const inclusiveOptions = await Array.fromAsync(\n object.getInclusiveOptions(ctx),\n );\n assert.deepStrictEqual(inclusiveOptions.length, 4);\n assert.ok(inclusiveOptions[0] instanceof Note);\n assert.deepStrictEqual(inclusiveOptions[0].name?.toString(), \"JavaScript\");\n assert.ok(inclusiveOptions[1] instanceof Note);\n assert.deepStrictEqual(inclusiveOptions[1].name?.toString(), \"TypeScript\");\n assert.ok(inclusiveOptions[2] instanceof Note);\n assert.deepStrictEqual(inclusiveOptions[2].name?.toString(), \"Python\");\n assert.ok(inclusiveOptions[3] instanceof Note);\n assert.deepStrictEqual(inclusiveOptions[3].name?.toString(), \"Rust\");\n\n assert.deepStrictEqual(poll.visibility, \"unlisted\");\n assert.deepStrictEqual(activity.toIds, [\n ctx.getFollowersUri(bot.identifier),\n ]);\n assert.deepStrictEqual(activity.ccIds, [PUBLIC_COLLECTION]);\n });\n\n await t.test(\"poll with direct visibility\", async () => {\n const mentioned = new Person({\n id: new URL(\"https://example.com/ap/actor/alice\"),\n preferredUsername: \"alice\",\n });\n ctx.sentActivities = [];\n const endTime = Temporal.Now.instant().add({ hours: 12 });\n const poll = await session.publish(\n text`Hey ${mention(mentioned)}, what do you think?`,\n {\n class: Question,\n poll: {\n multiple: false,\n options: [\"Good\", \"Bad\", \"Neutral\"],\n endTime,\n },\n visibility: \"direct\",\n },\n );\n assert.deepStrictEqual(ctx.sentActivities.length, 1);\n const { recipients, activity } = ctx.sentActivities[0];\n assert.deepStrictEqual(recipients, [mentioned]);\n assert.ok(activity instanceof Create);\n const object = await activity.getObject(ctx);\n assert.ok(object instanceof Question);\n assert.deepStrictEqual(object.toIds, [mentioned.id]);\n assert.deepStrictEqual(object.ccIds, []);\n assert.deepStrictEqual(poll.visibility, \"direct\");\n });\n\n await t.test(\"poll end-to-end workflow\", async () => {\n // Create fresh repository and session for isolation\n const freshRepository = new MemoryRepository();\n const freshBot = new BotImpl<void>({\n kv: new MemoryKvStore(),\n repository: freshRepository,\n username: \"testbot\",\n });\n const freshCtx = createMockContext(freshBot, \"https://example.com\");\n const freshSession = new SessionImpl(freshBot, freshCtx);\n\n const endTime = Temporal.Now.instant().add({ hours: 1 });\n\n // 1. Create a poll\n const poll = await freshSession.publish(\n text`What should we have for lunch?`,\n {\n class: Question,\n poll: {\n multiple: false,\n options: [\"Pizza\", \"Burgers\", \"Salad\"],\n endTime,\n },\n },\n );\n\n // Verify poll was created correctly\n assert.deepStrictEqual(freshCtx.sentActivities.length, 1);\n const { activity: createActivity } = freshCtx.sentActivities[0];\n assert.ok(createActivity instanceof Create);\n const pollObject = await createActivity.getObject(freshCtx);\n assert.ok(pollObject instanceof Question);\n assert.deepStrictEqual(pollObject.endTime, endTime);\n\n // Get poll options\n const options = await Array.fromAsync(\n pollObject.getExclusiveOptions(freshCtx),\n );\n assert.deepStrictEqual(options.length, 3);\n assert.deepStrictEqual(options[0].name?.toString(), \"Pizza\");\n assert.deepStrictEqual(options[1].name?.toString(), \"Burgers\");\n assert.deepStrictEqual(options[2].name?.toString(), \"Salad\");\n\n // 2. Verify poll is accessible via getOutbox\n const outbox = freshSession.getOutbox({ order: \"newest\" });\n const messages = await Array.fromAsync(outbox);\n assert.deepStrictEqual(messages.length, 1);\n assert.deepStrictEqual(messages[0].id, poll.id);\n assert.deepStrictEqual(messages[0].text, \"What should we have for lunch?\");\n\n // 3. Verify poll structure\n assert.deepStrictEqual(poll.visibility, \"public\");\n assert.deepStrictEqual(poll.mentions, []);\n });\n});\n\ntest(\"SessionImpl.getOutbox()\", async (t) => {\n const repository = new MemoryRepository();\n const bot = new BotImpl<void>({\n kv: new MemoryKvStore(),\n repository,\n username: \"bot\",\n });\n const ctx = createMockContext(bot, \"https://example.com\");\n const session = new SessionImpl(bot, ctx);\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 await repository.addMessage(\"01941f29-7c00-7fe8-ab0a-7b593990a3c0\", messageA);\n await repository.addMessage(\"0194244f-d800-7873-8993-ef71ccd47306\", messageB);\n await repository.addMessage(\"01942976-3400-7f34-872e-2cbf0f9eeac4\", messageC);\n await repository.addMessage(\"01942e9c-9000-7480-a553-7a6ce737ce14\", messageD);\n\n await t.test(\"default\", async () => {\n const outbox = session.getOutbox({ order: \"oldest\" });\n const messages = await Array.fromAsync(outbox);\n assert.deepStrictEqual(messages.length, 4);\n\n assert.deepStrictEqual(\n messages[0].id.href,\n \"https://example.com/ap/note/01941f29-7c00-7fe8-ab0a-7b593990a3c0\",\n );\n assert.deepStrictEqual(\n messages[0].actor.id?.href,\n \"https://example.com/ap/actor/bot\",\n );\n assert.deepStrictEqual(messages[0].visibility, \"unlisted\");\n assert.deepStrictEqual(messages[0].text, \"Hello, world!\");\n assert.deepStrictEqual(\n messages[0].published,\n Temporal.Instant.from(\"2025-01-01T00:00:00Z\"),\n );\n\n assert.deepStrictEqual(\n messages[1].id.href,\n \"https://example.com/ap/note/0194244f-d800-7873-8993-ef71ccd47306\",\n );\n assert.deepStrictEqual(\n messages[1].actor.id?.href,\n \"https://example.com/ap/actor/bot\",\n );\n assert.deepStrictEqual(messages[1].visibility, \"unlisted\");\n assert.deepStrictEqual(messages[1].text, \"Hello, world!\");\n assert.deepStrictEqual(\n messages[1].published,\n Temporal.Instant.from(\"2025-01-02T00:00:00Z\"),\n );\n\n assert.deepStrictEqual(\n messages[2].id.href,\n \"https://example.com/ap/note/01942976-3400-7f34-872e-2cbf0f9eeac4\",\n );\n assert.deepStrictEqual(\n messages[2].actor.id?.href,\n \"https://example.com/ap/actor/bot\",\n );\n assert.deepStrictEqual(messages[2].visibility, \"unlisted\");\n assert.deepStrictEqual(messages[2].text, \"Hello, world!\");\n assert.deepStrictEqual(\n messages[2].published,\n Temporal.Instant.from(\"2025-01-03T00:00:00Z\"),\n );\n\n assert.deepStrictEqual(\n messages[3].id.href,\n \"https://example.com/ap/note/01942e9c-9000-7480-a553-7a6ce737ce14\",\n );\n assert.deepStrictEqual(\n messages[3].actor.id?.href,\n \"https://example.com/ap/actor/bot\",\n );\n assert.deepStrictEqual(messages[3].visibility, \"unlisted\");\n assert.deepStrictEqual(messages[3].text, \"Hello, world!\");\n assert.deepStrictEqual(\n messages[3].published,\n Temporal.Instant.from(\"2025-01-04T00:00:00Z\"),\n );\n });\n\n await t.test(\"order: 'oldest'\", async () => {\n const outbox = session.getOutbox({ order: \"oldest\" });\n const messages = await Array.fromAsync(outbox);\n const messageIds = messages.map((msg) => msg.id.href);\n assert.deepStrictEqual(messageIds, [\n \"https://example.com/ap/note/01941f29-7c00-7fe8-ab0a-7b593990a3c0\",\n \"https://example.com/ap/note/0194244f-d800-7873-8993-ef71ccd47306\",\n \"https://example.com/ap/note/01942976-3400-7f34-872e-2cbf0f9eeac4\",\n \"https://example.com/ap/note/01942e9c-9000-7480-a553-7a6ce737ce14\",\n ]);\n });\n\n await t.test(\"order: 'newest'\", async () => {\n const outbox = session.getOutbox({ order: \"newest\" });\n const messages = await Array.fromAsync(outbox);\n const messageIds = messages.map((msg) => msg.id.href);\n assert.deepStrictEqual(messageIds, [\n \"https://example.com/ap/note/01942e9c-9000-7480-a553-7a6ce737ce14\",\n \"https://example.com/ap/note/01942976-3400-7f34-872e-2cbf0f9eeac4\",\n \"https://example.com/ap/note/0194244f-d800-7873-8993-ef71ccd47306\",\n \"https://example.com/ap/note/01941f29-7c00-7fe8-ab0a-7b593990a3c0\",\n ]);\n });\n\n await t.test(\"since\", async () => {\n const outbox = session.getOutbox({\n since: Temporal.Instant.from(\"2025-01-03T00:00:00Z\"),\n });\n const messages = await Array.fromAsync(outbox);\n const messageIds = messages.map((msg) => msg.id.href);\n assert.deepStrictEqual(messageIds, [\n \"https://example.com/ap/note/01942e9c-9000-7480-a553-7a6ce737ce14\",\n \"https://example.com/ap/note/01942976-3400-7f34-872e-2cbf0f9eeac4\",\n ]);\n });\n\n await t.test(\"until\", async () => {\n const outbox = session.getOutbox({\n until: Temporal.Instant.from(\"2025-01-02T00:00:00Z\"),\n });\n const messages = await Array.fromAsync(outbox);\n const messageIds = messages.map((msg) => msg.id.href);\n assert.deepStrictEqual(messageIds, [\n \"https://example.com/ap/note/0194244f-d800-7873-8993-ef71ccd47306\",\n \"https://example.com/ap/note/01941f29-7c00-7fe8-ab0a-7b593990a3c0\",\n ]);\n });\n\n await t.test(\"messages should have update and delete methods\", async () => {\n const outbox = session.getOutbox({ order: \"oldest\" });\n const messages = await Array.fromAsync(outbox);\n assert.strictEqual(messages.length, 4);\n\n for (const message of messages) {\n assert.strictEqual(typeof message.update, \"function\");\n assert.strictEqual(typeof message.delete, \"function\");\n }\n });\n});\n\nexport interface SentActivity {\n recipients: \"followers\" | Recipient[];\n activity: Activity;\n}\n\nexport interface MockContext extends Context<void> {\n sentActivities: SentActivity[];\n}\n\nexport function createMockContext(\n bot: BotImpl<void>,\n origin: URL | string,\n): MockContext {\n const ctx = bot.federation.createContext(\n new URL(origin),\n undefined,\n ) as MockContext;\n ctx.sentActivities = [];\n ctx.sendActivity = (_, recipients, activity) => {\n ctx.sentActivities.push({\n recipients: recipients === \"followers\"\n ? \"followers\"\n : Array.isArray(recipients)\n ? recipients\n : [recipients],\n activity,\n });\n return Promise.resolve();\n };\n return ctx;\n}\n"],"mappings":";;;;;;;;;;;;;;;AAoCA,KAAK,wBAAwB,OAAO,MAAM;CACxC,MAAM,aAAa,IAAI;CACvB,MAAM,MAAM,IAAI,QAAc;EAC5B,IAAI,IAAI;EACR;EACA,UAAU;CACX;CACD,MAAM,MAAM,kBAAkB,KAAK,sBAAsB;CACzD,MAAM,UAAU,IAAI,YAAY,KAAK;AAErC,OAAM,EAAE,KAAK,UAAU,YAAY;EACjC,MAAM,QAAQ,IAAI,OAAO;GACvB,IAAI,IAAI,IAAI;GACZ,mBAAmB;EACpB;AACD,QAAM,QAAQ,OAAO,MAAM;AAC3B,SAAO,gBAAgB,IAAI,eAAe,QAAQ,EAAE;EACpD,MAAM,EAAE,YAAY,UAAU,GAAG,IAAI,eAAe;AACpD,SAAO,gBAAgB,YAAY,CAAC,KAAM,EAAC;AAC3C,SAAO,GAAG,oBAAoB,OAAO;EACrC,MAAM,SAAS,IAAI,SAAS,SAAS,GAAG;AACxC,SAAO,gBAAgB,QAAQ,MAAM,SAAS;AAC9C,SAAO,GAAG,QAAQ,SAAS,SAAS;AACpC,SAAO,gBAAgB,OAAO,OAAO,OAAO;AAC5C,SAAO,gBAAgB,SAAS,SAAS,IAAI,YAAY,IAAI,WAAW,CAAC;AACzE,SAAO,gBAAgB,SAAS,UAAU,MAAM,GAAG;AACnD,SAAO,gBAAgB,SAAS,OAAO,CAAC,MAAM,EAAG,EAAC;EAClD,MAAM,SAAS,MAAM,WAAW,cAAc,OAAO,OAAO,GAAW;AACvE,SAAO,GAAG,UAAU,KAAK;AACzB,SAAO,gBACL,MAAM,OAAO,SAAS,EAAE,QAAQ,UAAW,EAAC,EAC5C,MAAM,SAAS,SAAS,EAAE,QAAQ,UAAW,EAAC,CAC/C;CACF,EAAC;AAEF,OAAM,EAAE,KAAK,gBAAgB,YAAY;AACvC,MAAI,iBAAiB,CAAE;AACvB,QAAM,WAAW,YACf,IAAI,IAAI,uCACR,IAAI,OAAO;GACT,IAAI,IAAI,IACN;GAEF,OAAO,IAAI,IAAI;GACf,QAAQ,IAAI,IAAI;EACjB,GACF;EACD,MAAM,QAAQ,IAAI,OAAO;GACvB,IAAI,IAAI,IAAI;GACZ,mBAAmB;EACpB;AACD,QAAM,QAAQ,OAAO,MAAM;AAC3B,SAAO,gBAAgB,IAAI,gBAAgB,CAAE,EAAC;CAC/C,EAAC;AAEF,OAAM,EAAE,KAAK,qBAAqB,YAAY;AAC5C,MAAI,iBAAiB,CAAE;AACvB,QAAM,OAAO,QACX,MAAM,QAAQ,OAAO,QAAQ,QAAQ,KAAK,EAC1C,WACA,gCACD;AACD,SAAO,gBAAgB,IAAI,gBAAgB,CAAE,EAAC;AAE9C,QAAM,OAAO,QACX,MAAM,QAAQ,OAAO,QAAQ,QAAQ,EACrC,WACA,gCACD;AACD,SAAO,gBAAgB,IAAI,gBAAgB,CAAE,EAAC;AAE9C,QAAM,OAAO,QACX,MAAM,QAAQ,OAAO,QAAQ,YAAY,EACzC,WACA,gCACD;AACD,SAAO,gBAAgB,IAAI,gBAAgB,CAAE,EAAC;EAE9C,MAAM,QAAQ,MAAM,QAAQ,UAAU;AACtC,QAAM,OAAO,QACX,MAAM,QAAQ,OAAO,MAAM,EAC3B,WACA,gCACD;AACD,SAAO,gBAAgB,IAAI,gBAAgB,CAAE,EAAC;CAC/C,EAAC;AACH,EAAC;AAEF,KAAK,0BAA0B,OAAO,MAAM;CAC1C,MAAM,aAAa,IAAI;CACvB,MAAM,MAAM,IAAI,QAAc;EAC5B,IAAI,IAAI;EACR;EACA,UAAU;CACX;CACD,MAAM,MAAM,kBAAkB,KAAK,sBAAsB;CACzD,MAAM,UAAU,IAAI,YAAY,KAAK;AAErC,OAAM,EAAE,KAAK,YAAY,YAAY;AACnC,QAAM,WAAW,YACf,IAAI,IAAI,uCACR,IAAI,OAAO;GACT,IAAI,IAAI,IACN;GAEF,OAAO,IAAI,IAAI;GACf,QAAQ,IAAI,IAAI;EACjB,GACF;EACD,MAAM,QAAQ,IAAI,OAAO;GACvB,IAAI,IAAI,IAAI;GACZ,mBAAmB;EACpB;AACD,QAAM,QAAQ,SAAS,MAAM;AAC7B,SAAO,gBAAgB,IAAI,eAAe,QAAQ,EAAE;EACpD,MAAM,EAAE,YAAY,UAAU,GAAG,IAAI,eAAe;AACpD,SAAO,gBAAgB,YAAY,CAAC,KAAM,EAAC;AAC3C,SAAO,GAAG,oBAAoB,KAAK;EACnC,MAAM,SAAS,MAAM,SAAS,UAAU,IAAI;AAC5C,SAAO,GAAG,kBAAkB,OAAO;AACnC,SAAO,gBACL,OAAO,IACP,IAAI,IACF,sEAEH;AACD,SAAO,gBAAgB,OAAO,SAAS,IAAI,YAAY,IAAI,WAAW,CAAC;AACvE,SAAO,gBACL,MAAM,WAAW,YACf,IAAI,IAAI,sCACT,SAEF;CACF,EAAC;AAEF,OAAM,EAAE,KAAK,kBAAkB,YAAY;AACzC,MAAI,iBAAiB,CAAE;EACvB,MAAM,QAAQ,IAAI,OAAO;GACvB,IAAI,IAAI,IAAI;GACZ,mBAAmB;EACpB;AACD,QAAM,QAAQ,SAAS,MAAM;AAC7B,SAAO,gBAAgB,IAAI,gBAAgB,CAAE,EAAC;CAC/C,EAAC;AAEF,OAAM,EAAE,KAAK,uBAAuB,YAAY;AAC9C,MAAI,iBAAiB,CAAE;AACvB,QAAM,OAAO,QACX,MAAM,QAAQ,SAAS,QAAQ,QAAQ,KAAK,EAC5C,WACA,kCACD;AACD,SAAO,gBAAgB,IAAI,gBAAgB,CAAE,EAAC;AAE9C,QAAM,OAAO,QACX,MAAM,QAAQ,SAAS,QAAQ,QAAQ,EACvC,WACA,kCACD;AACD,SAAO,gBAAgB,IAAI,gBAAgB,CAAE,EAAC;AAE9C,QAAM,OAAO,QACX,MAAM,QAAQ,SAAS,QAAQ,YAAY,EAC3C,WACA,kCACD;AACD,SAAO,gBAAgB,IAAI,gBAAgB,CAAE,EAAC;EAE9C,MAAM,QAAQ,MAAM,QAAQ,UAAU;AACtC,QAAM,OAAO,QACX,MAAM,QAAQ,SAAS,MAAM,EAC7B,WACA,kCACD;AACD,SAAO,gBAAgB,IAAI,gBAAgB,CAAE,EAAC;CAC/C,EAAC;AACH,EAAC;AAEF,SAAS,yBAAyB,MAAM;CACtC,MAAM,aAAa,IAAI;CACvB,MAAM,MAAM,IAAI,QAAc;EAC5B,IAAI,IAAI;EACR;EACA,UAAU;CACX;CACD,MAAM,MAAM,kBAAkB,KAAK,sBAAsB;CACzD,MAAM,UAAU,IAAI,YAAY,KAAK;AAErC,MAAK,mBAAmB,YAAY;EAClC,MAAM,aAAa,IAAI,IAAI;EAC3B,MAAM,WAAW,IAAI,OAAO;GAC1B,IAAI;GACJ,mBAAmB;EACpB;AACD,QAAM,WAAW,YACf,IAAI,IAAI,uCACR,IAAI,OAAO;GACT,IAAI,IAAI,IACN;GAEF,OAAO,IAAI,IAAI;GACf,QAAQ;EACT,GACF;AACD,SAAO,GAAG,MAAM,QAAQ,QAAQ,WAAW,KAAK,CAAC;AACjD,SAAO,GAAG,MAAM,QAAQ,QAAQ,WAAW,CAAC;AAC5C,SAAO,GAAG,MAAM,QAAQ,QAAQ,SAAS,CAAC;CAC3C,EAAC;AAEF,MAAK,2BAA2B,YAAY;EAC1C,MAAM,UAAU,IAAI,IAAI;EACxB,MAAM,QAAQ,IAAI,OAAO;GACvB,IAAI;GACJ,mBAAmB;EACpB;AACD,SAAO,gBAAgB,MAAM,QAAQ,QAAQ,QAAQ,KAAK,EAAE,MAAM;AAClE,SAAO,gBAAgB,MAAM,QAAQ,QAAQ,QAAQ,EAAE,MAAM;AAC7D,SAAO,gBAAgB,MAAM,QAAQ,QAAQ,MAAM,EAAE,MAAM;CAC5D,EAAC;AAEF,MAAK,cAAc,YAAY;AAC7B,SAAO,gBAAgB,MAAM,QAAQ,QAAQ,QAAQ,QAAQ,KAAK,EAAE,MAAM;AAC1E,SAAO,gBAAgB,MAAM,QAAQ,QAAQ,QAAQ,QAAQ,EAAE,MAAM;AACrE,SAAO,gBACL,MAAM,QAAQ,QAAQ,MAAM,QAAQ,UAAU,CAAC,EAC/C,MACD;AACD,SAAO,gBAAgB,MAAM,QAAQ,QAAQ,QAAQ,YAAY,EAAE,MAAM;CAC1E,EAAC;AACH,EAAC;AAEF,KAAK,kCAAkC,YAAY;CACjD,MAAM,MAAM,IAAI,QAAc;EAC5B,IAAI,IAAI;EACR,UAAU;EACV,MAAM;EACN,SAAS,KAAK;EACd,MAAM,IAAI,IAAI;EACd,OAAO,IAAI,IAAI;CAChB;CACD,MAAM,MAAM,kBAAkB,KAAK,sBAAsB;CACzD,MAAM,UAAU,IAAI,YAAY,KAAK;AAErC,OAAM,QAAQ,kBAAkB;AAEhC,QAAO,gBAAgB,IAAI,eAAe,QAAQ,EAAE;CACpD,MAAM,EAAE,YAAY,UAAU,GAAG,IAAI,eAAe;AACpD,QAAO,gBAAgB,YAAY,YAAY;AAC/C,QAAO,GAAG,oBAAoB,OAAO;AACrC,QAAO,gBAAgB,SAAS,SAAS,IAAI,YAAY,IAAI,WAAW,CAAC;AACzE,QAAO,gBAAgB,SAAS,OAAO,CAAC,IAAI,gBAAgB,IAAI,WAAW,AAAC,EAAC;AAC7E,QAAO,gBAAgB,SAAS,OAAO,CAAE,EAAC;CAC1C,MAAM,QAAQ,MAAM,SAAS,UAAU,IAAI;AAC3C,QAAO,GAAG,iBAAiB,IAAI,MAAM;AACrC,QAAO,gBAAgB,MAAM,IAAI,QAAQ,QAAQ;AACjD,QAAO,gBAAgB,MAAM,MAAM,UAAU,EAAE,mBAAmB;AAClE,QAAO,gBACL,MAAM,SAAS,UAAU,EACzB,kCACD;CACD,MAAM,OAAO,MAAM,MAAM,SAAS;AAClC,QAAO,GAAG,QAAQ,KAAK;AACvB,QAAO,gBAAgB,KAAK,KAAK,IAAI,IAAI,gCAAgC;CACzE,MAAM,QAAQ,MAAM,MAAM,UAAU;AACpC,QAAO,GAAG,SAAS,KAAK;AACxB,QAAO,gBAAgB,MAAM,KAAK,IAAI,IAAI,kCAAkC;AAC7E,EAAC;AAEF,KAAK,yBAAyB,OAAO,MAAM;CACzC,MAAM,KAAK,IAAI;CACf,MAAM,MAAM,IAAI,QAAc;EAAE;EAAI,UAAU;CAAO;CACrD,MAAM,MAAM,kBAAkB,KAAK,sBAAsB;CACzD,MAAM,UAAU,IAAI,YAAY,KAAK;AAErC,OAAM,EAAE,KAAK,UAAU,YAAY;AACjC,MAAI,iBAAiB,CAAE;EACvB,MAAM,YAAY,MAAM,QAAQ,QAAQ,KAAK,eAAe;AAC5D,SAAO,gBAAgB,IAAI,eAAe,QAAQ,EAAE;EACpD,MAAM,EAAE,YAAY,UAAU,GAAG,IAAI,eAAe;AACpD,SAAO,gBAAgB,YAAY,YAAY;AAC/C,SAAO,GAAG,oBAAoB,OAAO;AACrC,SAAO,gBAAgB,SAAS,SAAS,IAAI,YAAY,IAAI,WAAW,CAAC;AACzE,SAAO,gBAAgB,SAAS,OAAO,CAAC,iBAAkB,EAAC;AAC3D,SAAO,gBAAgB,SAAS,OAAO,CACrC,IAAI,gBAAgB,IAAI,WAAW,AACpC,EAAC;EACF,MAAM,SAAS,MAAM,SAAS,UAAU,IAAI;AAC5C,SAAO,GAAG,kBAAkB,KAAK;AACjC,SAAO,gBACL,OAAO,eACP,IAAI,YAAY,IAAI,WAAW,CAChC;AACD,SAAO,gBAAgB,OAAO,OAAO,CAAC,iBAAkB,EAAC;AACzD,SAAO,gBAAgB,OAAO,OAAO,CAAC,IAAI,gBAAgB,IAAI,WAAW,AAAC,EAAC;AAC3E,SAAO,gBAAgB,OAAO,SAAS,uBAAuB;AAC9D,SAAO,gBAAgB,OAAO,QAAQ,CAAE,EAAC;AACzC,SAAO,gBAAgB,UAAU,IAAI,OAAO,GAAG;AAC/C,SAAO,gBAAgB,UAAU,MAAM,gBAAgB;AACvD,SAAO,gBAAgB,UAAU,MAAM,uBAAuB;AAC9D,SAAO,gBAAgB,UAAU,YAAY,SAAS;AACtD,SAAO,gBAAgB,UAAU,UAAU,CAAE,EAAC;CAC/C,EAAC;AAEF,OAAM,EAAE,KAAK,YAAY,YAAY;AACnC,MAAI,iBAAiB,CAAE;EACvB,MAAM,cAAc,MAAM,QAAQ,QAAQ,KAAK,SAAS,EACtD,YAAY,WACb,EAAC;AACF,SAAO,gBAAgB,IAAI,eAAe,QAAQ,EAAE;EACpD,MAAM,EAAE,YAAY,UAAU,GAAG,IAAI,eAAe;AACpD,SAAO,gBAAgB,YAAY,YAAY;AAC/C,SAAO,GAAG,oBAAoB,OAAO;AACrC,SAAO,gBAAgB,SAAS,SAAS,IAAI,YAAY,IAAI,WAAW,CAAC;AACzE,SAAO,gBAAgB,SAAS,OAAO,CACrC,IAAI,gBAAgB,IAAI,WAAW,AACpC,EAAC;AACF,SAAO,gBAAgB,SAAS,OAAO,CAAC,iBAAkB,EAAC;EAC3D,MAAM,SAAS,MAAM,SAAS,UAAU,IAAI;AAC5C,SAAO,GAAG,kBAAkB,KAAK;AACjC,SAAO,gBACL,OAAO,eACP,IAAI,YAAY,IAAI,WAAW,CAChC;AACD,SAAO,gBAAgB,OAAO,OAAO,CAAC,IAAI,gBAAgB,IAAI,WAAW,AAAC,EAAC;AAC3E,SAAO,gBAAgB,OAAO,OAAO,CAAC,iBAAkB,EAAC;AACzD,SAAO,gBAAgB,OAAO,SAAS,gBAAgB;AACvD,SAAO,gBAAgB,OAAO,QAAQ,CAAE,EAAC;AACzC,SAAO,gBAAgB,YAAY,IAAI,OAAO,GAAG;AACjD,SAAO,gBAAgB,YAAY,MAAM,SAAS;AAClD,SAAO,gBAAgB,YAAY,MAAM,gBAAgB;AACzD,SAAO,gBAAgB,YAAY,YAAY,WAAW;AAC1D,SAAO,gBAAgB,YAAY,UAAU,CAAE,EAAC;CACjD,EAAC;AAEF,OAAM,EAAE,KAAK,aAAa,YAAY;AACpC,MAAI,iBAAiB,CAAE;EACvB,MAAM,eAAe,MAAM,QAAQ,QAAQ,KAAK,MAAM,EACpD,YAAY,YACb,EAAC;AACF,SAAO,gBAAgB,IAAI,eAAe,QAAQ,EAAE;EACpD,MAAM,EAAE,YAAY,UAAU,GAAG,IAAI,eAAe;AACpD,SAAO,gBAAgB,YAAY,YAAY;AAC/C,SAAO,GAAG,oBAAoB,OAAO;AACrC,SAAO,gBAAgB,SAAS,SAAS,IAAI,YAAY,IAAI,WAAW,CAAC;AACzE,SAAO,gBAAgB,SAAS,OAAO,CACrC,IAAI,gBAAgB,IAAI,WAAW,AACpC,EAAC;AACF,SAAO,gBAAgB,SAAS,OAAO,CAAE,EAAC;EAC1C,MAAM,SAAS,MAAM,SAAS,UAAU,IAAI;AAC5C,SAAO,GAAG,kBAAkB,KAAK;AACjC,SAAO,gBACL,OAAO,eACP,IAAI,YAAY,IAAI,WAAW,CAChC;AACD,SAAO,gBAAgB,OAAO,OAAO,CAAC,IAAI,gBAAgB,IAAI,WAAW,AAAC,EAAC;AAC3E,SAAO,gBAAgB,OAAO,OAAO,CAAE,EAAC;AACxC,SAAO,gBAAgB,OAAO,SAAS,aAAa;AACpD,SAAO,gBAAgB,OAAO,QAAQ,CAAE,EAAC;AACzC,SAAO,gBAAgB,aAAa,IAAI,OAAO,GAAG;AAClD,SAAO,gBAAgB,aAAa,MAAM,MAAM;AAChD,SAAO,gBAAgB,aAAa,MAAM,aAAa;AACvD,SAAO,gBAAgB,aAAa,YAAY,YAAY;AAC5D,SAAO,gBAAgB,aAAa,UAAU,CAAE,EAAC;CAClD,EAAC;AAEF,OAAM,EAAE,KAAK,UAAU,YAAY;EACjC,MAAM,YAAY,IAAI,OAAO;GAC3B,IAAI,IAAI,IAAI;GACZ,mBAAmB;EACpB;AACD,MAAI,iBAAiB,CAAE;EACvB,MAAM,YAAY,MAAM,QAAQ,QAC9B,KAAK,MAAM,QAAQ,UAAU,CAAC,IAC9B,EAAE,YAAY,SAAU,EACzB;AACD,SAAO,gBAAgB,IAAI,eAAe,QAAQ,EAAE;EACpD,MAAM,EAAE,YAAY,UAAU,GAAG,IAAI,eAAe;AACpD,SAAO,gBAAgB,YAAY,CAAC,SAAU,EAAC;AAC/C,SAAO,GAAG,oBAAoB,OAAO;AACrC,SAAO,gBAAgB,SAAS,SAAS,IAAI,YAAY,IAAI,WAAW,CAAC;AACzE,SAAO,gBAAgB,SAAS,OAAO,CAAC,UAAU,EAAG,EAAC;AACtD,SAAO,gBAAgB,SAAS,OAAO,CAAE,EAAC;EAC1C,MAAM,SAAS,MAAM,SAAS,UAAU,IAAI;AAC5C,SAAO,GAAG,kBAAkB,KAAK;AACjC,SAAO,gBACL,OAAO,eACP,IAAI,YAAY,IAAI,WAAW,CAChC;AACD,SAAO,gBAAgB,OAAO,OAAO,CAAC,UAAU,EAAG,EAAC;AACpD,SAAO,gBAAgB,OAAO,OAAO,CAAE,EAAC;AACxC,SAAO,gBACL,OAAO,SACP,iKAGD;EACD,MAAM,OAAO,MAAM,MAAM,UAAU,OAAO,SAAS,CAAC;AACpD,SAAO,gBAAgB,KAAK,QAAQ,EAAE;AACtC,SAAO,gBAAgB,UAAU,IAAI,OAAO,GAAG;AAC/C,SAAO,gBAAgB,UAAU,MAAM,yBAAyB;AAChE,SAAO,gBAAgB,UAAU,MAAM,OAAO,QAAQ;AACtD,SAAO,gBAAgB,UAAU,YAAY,SAAS;CAEvD,EAAC;AAEF,OAAM,EAAE,KAAK,SAAS,YAAY;EAChC,MAAM,iBAAiB,IAAI,OAAO;GAChC,IAAI,IAAI,IAAI;GACZ,mBAAmB;EACpB;EACD,MAAM,eAAe,IAAI,KAAK;GAC5B,IAAI,IAAI,IACN;GAEF,SAAS;GACT,aAAa;GACb,IAAI,IAAI,IAAI;GACZ,IAAI;EACL;EACD,MAAM,cAAc,MAAM,cACxB,cACA,SACA,CAAE,EACH;AACD,MAAI,iBAAiB,CAAE;EACvB,MAAM,QAAQ,MAAM,QAAQ,QAAQ,KAAK,kBAAkB,EACzD,aAAa,YACd,EAAC;AACF,SAAO,gBAAgB,IAAI,eAAe,QAAQ,EAAE;EACpD,MAAM,EAAE,YAAY,UAAU,GAAG,IAAI,eAAe;AACpD,SAAO,gBAAgB,YAAY,YAAY;AAC/C,SAAO,GAAG,oBAAoB,OAAO;AACrC,SAAO,gBAAgB,SAAS,SAAS,IAAI,YAAY,IAAI,WAAW,CAAC;AACzE,SAAO,gBAAgB,SAAS,OAAO,CAAC,iBAAkB,EAAC;AAC3D,SAAO,gBAAgB,SAAS,OAAO,CACrC,IAAI,gBAAgB,IAAI,WAAW,AACpC,EAAC;EACF,MAAM,SAAS,MAAM,SAAS,UAAU,IAAI;EAC5C,MAAM,EAAE,YAAY,aAAa,UAAU,WAAW,GACpD,IAAI,eAAe;AACrB,SAAO,gBAAgB,aAAa,CAAC,cAAe,EAAC;AACrD,SAAO,GAAG,qBAAqB,OAAO;AACtC,SAAO,gBAAgB,UAAU,SAAS,IAAI,YAAY,IAAI,WAAW,CAAC;AAC1E,SAAO,gBAAgB,UAAU,OAAO,CAAC,iBAAkB,EAAC;AAC5D,SAAO,gBAAgB,UAAU,OAAO,CACtC,IAAI,gBAAgB,IAAI,WAAW,AACpC,EAAC;AACF,SAAO,GAAG,kBAAkB,KAAK;AACjC,SAAO,gBACL,OAAO,eACP,IAAI,YAAY,IAAI,WAAW,CAChC;AACD,SAAO,gBAAgB,OAAO,OAAO,CAAC,iBAAkB,EAAC;AACzD,SAAO,gBAAgB,OAAO,OAAO,CAAC,IAAI,gBAAgB,IAAI,WAAW,AAAC,EAAC;AAC3E,SAAO,gBACL,OAAO,UACN;;2CAEoC,YAAY,GAAG,KAAK,IAAI,YAAY,GAAG,KAAK,UAClF;AACD,SAAO,gBAAgB,OAAO,UAAU,YAAY,GAAG;AACvD,SAAO,gBAAgB,MAAM,IAAI,OAAO,GAAG;AAC3C,SAAO,gBACL,MAAM,OACL,yBAAyB,YAAY,GAAG,KAAK,EAC/C;AACD,SAAO,gBACL,MAAM,OACL;;sBAEe,YAAY,GAAG,KAAK,IAAI,YAAY,GAAG,KAAK,UAC7D;AACD,SAAO,gBAAgB,MAAM,YAAY,SAAS;AAClD,SAAO,gBAAgB,MAAM,aAAa,IAAI,YAAY,GAAG;CAC9D,EAAC;AAEF,OAAM,EAAE,KAAK,sBAAsB,YAAY;AAC7C,MAAI,iBAAiB,CAAE;EACvB,MAAM,UAAU,SAAS,IAAI,SAAS,CAAC,IAAI,EAAE,OAAO,GAAI,EAAC;EACzD,MAAM,OAAO,MAAM,QAAQ,QAAQ,KAAK,8BAA8B;GACpE,OAAO;GACP,MAAM;IACJ,UAAU;IACV,SAAS;KAAC;KAAO;KAAQ;IAAQ;IACjC;GACD;EACF,EAAC;AACF,SAAO,gBAAgB,IAAI,eAAe,QAAQ,EAAE;EACpD,MAAM,EAAE,YAAY,UAAU,GAAG,IAAI,eAAe;AACpD,SAAO,gBAAgB,YAAY,YAAY;AAC/C,SAAO,GAAG,oBAAoB,OAAO;AACrC,SAAO,gBAAgB,SAAS,SAAS,IAAI,YAAY,IAAI,WAAW,CAAC;AACzE,SAAO,gBAAgB,SAAS,OAAO,CAAC,iBAAkB,EAAC;AAC3D,SAAO,gBAAgB,SAAS,OAAO,CACrC,IAAI,gBAAgB,IAAI,WAAW,AACpC,EAAC;EACF,MAAM,SAAS,MAAM,SAAS,UAAU,IAAI;AAC5C,SAAO,GAAG,kBAAkB,SAAS;AACrC,SAAO,gBACL,OAAO,eACP,IAAI,YAAY,IAAI,WAAW,CAChC;AACD,SAAO,gBAAgB,OAAO,OAAO,CAAC,iBAAkB,EAAC;AACzD,SAAO,gBAAgB,OAAO,OAAO,CAAC,IAAI,gBAAgB,IAAI,WAAW,AAAC,EAAC;AAC3E,SAAO,gBACL,OAAO,SACP,0CACD;AACD,SAAO,gBAAgB,OAAO,SAAS,QAAQ;AAC/C,SAAO,gBAAgB,OAAO,QAAQ,EAAE;AACxC,SAAO,gBAAgB,OAAO,oBAAoB,CAAE,EAAC;EAErD,MAAM,mBAAmB,MAAM,MAAM,UACnC,OAAO,oBAAoB,IAAI,CAChC;AACD,SAAO,gBAAgB,iBAAiB,QAAQ,EAAE;AAClD,SAAO,GAAG,iBAAiB,cAAc,KAAK;AAC9C,SAAO,gBAAgB,iBAAiB,GAAG,MAAM,UAAU,EAAE,MAAM;AACnE,SAAO,GAAG,iBAAiB,cAAc,KAAK;AAC9C,SAAO,gBAAgB,iBAAiB,GAAG,MAAM,UAAU,EAAE,OAAO;AACpE,SAAO,GAAG,iBAAiB,cAAc,KAAK;AAC9C,SAAO,gBAAgB,iBAAiB,GAAG,MAAM,UAAU,EAAE,QAAQ;AAErE,OAAK,MAAM,UAAU,kBAAkB;GACrC,MAAM,UAAU,MAAM,OAAO,WAAW,IAAI;AAC5C,UAAO,gBAAgB,SAAS,YAAY,EAAE;EAC/C;AAED,SAAO,gBAAgB,KAAK,IAAI,OAAO,GAAG;AAC1C,SAAO,gBAAgB,KAAK,MAAM,8BAA8B;AAChE,SAAO,gBACL,KAAK,MACL,0CACD;AACD,SAAO,gBAAgB,KAAK,YAAY,SAAS;CAClD,EAAC;AAEF,OAAM,EAAE,KAAK,wBAAwB,YAAY;AAC/C,MAAI,iBAAiB,CAAE;EACvB,MAAM,UAAU,SAAS,IAAI,SAAS,CAAC,IAAI,EAAE,OAAO,KAAK,EAAG,EAAC;EAC7D,MAAM,OAAO,MAAM,QAAQ,QACzB,KAAK,2CACL;GACE,OAAO;GACP,MAAM;IACJ,UAAU;IACV,SAAS;KAAC;KAAc;KAAc;KAAU;IAAO;IACvD;GACD;GACD,YAAY;EACb,EACF;AACD,SAAO,gBAAgB,IAAI,eAAe,QAAQ,EAAE;EACpD,MAAM,EAAE,YAAY,UAAU,GAAG,IAAI,eAAe;AACpD,SAAO,gBAAgB,YAAY,YAAY;AAC/C,SAAO,GAAG,oBAAoB,OAAO;EACrC,MAAM,SAAS,MAAM,SAAS,UAAU,IAAI;AAC5C,SAAO,GAAG,kBAAkB,SAAS;AACrC,SAAO,gBAAgB,OAAO,SAAS,QAAQ;AAC/C,SAAO,gBAAgB,OAAO,QAAQ,EAAE;AACxC,SAAO,gBAAgB,OAAO,oBAAoB,CAAE,EAAC;EAErD,MAAM,mBAAmB,MAAM,MAAM,UACnC,OAAO,oBAAoB,IAAI,CAChC;AACD,SAAO,gBAAgB,iBAAiB,QAAQ,EAAE;AAClD,SAAO,GAAG,iBAAiB,cAAc,KAAK;AAC9C,SAAO,gBAAgB,iBAAiB,GAAG,MAAM,UAAU,EAAE,aAAa;AAC1E,SAAO,GAAG,iBAAiB,cAAc,KAAK;AAC9C,SAAO,gBAAgB,iBAAiB,GAAG,MAAM,UAAU,EAAE,aAAa;AAC1E,SAAO,GAAG,iBAAiB,cAAc,KAAK;AAC9C,SAAO,gBAAgB,iBAAiB,GAAG,MAAM,UAAU,EAAE,SAAS;AACtE,SAAO,GAAG,iBAAiB,cAAc,KAAK;AAC9C,SAAO,gBAAgB,iBAAiB,GAAG,MAAM,UAAU,EAAE,OAAO;AAEpE,SAAO,gBAAgB,KAAK,YAAY,WAAW;AACnD,SAAO,gBAAgB,SAAS,OAAO,CACrC,IAAI,gBAAgB,IAAI,WAAW,AACpC,EAAC;AACF,SAAO,gBAAgB,SAAS,OAAO,CAAC,iBAAkB,EAAC;CAC5D,EAAC;AAEF,OAAM,EAAE,KAAK,+BAA+B,YAAY;EACtD,MAAM,YAAY,IAAI,OAAO;GAC3B,IAAI,IAAI,IAAI;GACZ,mBAAmB;EACpB;AACD,MAAI,iBAAiB,CAAE;EACvB,MAAM,UAAU,SAAS,IAAI,SAAS,CAAC,IAAI,EAAE,OAAO,GAAI,EAAC;EACzD,MAAM,OAAO,MAAM,QAAQ,QACzB,KAAK,MAAM,QAAQ,UAAU,CAAC,uBAC9B;GACE,OAAO;GACP,MAAM;IACJ,UAAU;IACV,SAAS;KAAC;KAAQ;KAAO;IAAU;IACnC;GACD;GACD,YAAY;EACb,EACF;AACD,SAAO,gBAAgB,IAAI,eAAe,QAAQ,EAAE;EACpD,MAAM,EAAE,YAAY,UAAU,GAAG,IAAI,eAAe;AACpD,SAAO,gBAAgB,YAAY,CAAC,SAAU,EAAC;AAC/C,SAAO,GAAG,oBAAoB,OAAO;EACrC,MAAM,SAAS,MAAM,SAAS,UAAU,IAAI;AAC5C,SAAO,GAAG,kBAAkB,SAAS;AACrC,SAAO,gBAAgB,OAAO,OAAO,CAAC,UAAU,EAAG,EAAC;AACpD,SAAO,gBAAgB,OAAO,OAAO,CAAE,EAAC;AACxC,SAAO,gBAAgB,KAAK,YAAY,SAAS;CAClD,EAAC;AAEF,OAAM,EAAE,KAAK,4BAA4B,YAAY;EAEnD,MAAM,kBAAkB,IAAI;EAC5B,MAAM,WAAW,IAAI,QAAc;GACjC,IAAI,IAAI;GACR,YAAY;GACZ,UAAU;EACX;EACD,MAAM,WAAW,kBAAkB,UAAU,sBAAsB;EACnE,MAAM,eAAe,IAAI,YAAY,UAAU;EAE/C,MAAM,UAAU,SAAS,IAAI,SAAS,CAAC,IAAI,EAAE,OAAO,EAAG,EAAC;EAGxD,MAAM,OAAO,MAAM,aAAa,QAC9B,KAAK,iCACL;GACE,OAAO;GACP,MAAM;IACJ,UAAU;IACV,SAAS;KAAC;KAAS;KAAW;IAAQ;IACtC;GACD;EACF,EACF;AAGD,SAAO,gBAAgB,SAAS,eAAe,QAAQ,EAAE;EACzD,MAAM,EAAE,UAAU,gBAAgB,GAAG,SAAS,eAAe;AAC7D,SAAO,GAAG,0BAA0B,OAAO;EAC3C,MAAM,aAAa,MAAM,eAAe,UAAU,SAAS;AAC3D,SAAO,GAAG,sBAAsB,SAAS;AACzC,SAAO,gBAAgB,WAAW,SAAS,QAAQ;EAGnD,MAAM,UAAU,MAAM,MAAM,UAC1B,WAAW,oBAAoB,SAAS,CACzC;AACD,SAAO,gBAAgB,QAAQ,QAAQ,EAAE;AACzC,SAAO,gBAAgB,QAAQ,GAAG,MAAM,UAAU,EAAE,QAAQ;AAC5D,SAAO,gBAAgB,QAAQ,GAAG,MAAM,UAAU,EAAE,UAAU;AAC9D,SAAO,gBAAgB,QAAQ,GAAG,MAAM,UAAU,EAAE,QAAQ;EAG5D,MAAM,SAAS,aAAa,UAAU,EAAE,OAAO,SAAU,EAAC;EAC1D,MAAM,WAAW,MAAM,MAAM,UAAU,OAAO;AAC9C,SAAO,gBAAgB,SAAS,QAAQ,EAAE;AAC1C,SAAO,gBAAgB,SAAS,GAAG,IAAI,KAAK,GAAG;AAC/C,SAAO,gBAAgB,SAAS,GAAG,MAAM,iCAAiC;AAG1E,SAAO,gBAAgB,KAAK,YAAY,SAAS;AACjD,SAAO,gBAAgB,KAAK,UAAU,CAAE,EAAC;CAC1C,EAAC;AACH,EAAC;AAEF,KAAK,2BAA2B,OAAO,MAAM;CAC3C,MAAM,aAAa,IAAI;CACvB,MAAM,MAAM,IAAI,QAAc;EAC5B,IAAI,IAAI;EACR;EACA,UAAU;CACX;CACD,MAAM,MAAM,kBAAkB,KAAK,sBAAsB;CACzD,MAAM,UAAU,IAAI,YAAY,KAAK;CAErC,MAAM,WAAW,IAAI,OAAO;EAC1B,IAAI,IAAI,IACN;EAEF,OAAO,IAAI,IAAI;EACf,IAAI,IAAI,IAAI;EACZ,IAAI;EACJ,QAAQ,IAAI,KAAK;GACf,IAAI,IAAI,IACN;GAEF,aAAa,IAAI,IAAI;GACrB,IAAI,IAAI,IAAI;GACZ,IAAI;GACJ,SAAS;GACT,WAAW,SAAS,QAAQ,KAAK,uBAAuB;EACzD;EACD,WAAW,SAAS,QAAQ,KAAK,uBAAuB;CACzD;CACD,MAAM,WAAW,IAAI,OAAO;EAC1B,IAAI,IAAI,IACN;EAEF,OAAO,IAAI,IAAI;EACf,IAAI,IAAI,IAAI;EACZ,IAAI;EACJ,QAAQ,IAAI,KAAK;GACf,IAAI,IAAI,IACN;GAEF,aAAa,IAAI,IAAI;GACrB,IAAI,IAAI,IAAI;GACZ,IAAI;GACJ,SAAS;GACT,WAAW,SAAS,QAAQ,KAAK,uBAAuB;EACzD;EACD,WAAW,SAAS,QAAQ,KAAK,uBAAuB;CACzD;CACD,MAAM,WAAW,IAAI,OAAO;EAC1B,IAAI,IAAI,IACN;EAEF,OAAO,IAAI,IAAI;EACf,IAAI,IAAI,IAAI;EACZ,IAAI;EACJ,QAAQ,IAAI,KAAK;GACf,IAAI,IAAI,IACN;GAEF,aAAa,IAAI,IAAI;GACrB,IAAI,IAAI,IAAI;GACZ,IAAI;GACJ,SAAS;GACT,WAAW,SAAS,QAAQ,KAAK,uBAAuB;EACzD;EACD,WAAW,SAAS,QAAQ,KAAK,uBAAuB;CACzD;CACD,MAAM,WAAW,IAAI,OAAO;EAC1B,IAAI,IAAI,IACN;EAEF,OAAO,IAAI,IAAI;EACf,IAAI,IAAI,IAAI;EACZ,IAAI;EACJ,QAAQ,IAAI,KAAK;GACf,IAAI,IAAI,IACN;GAEF,aAAa,IAAI,IAAI;GACrB,IAAI,IAAI,IAAI;GACZ,IAAI;GACJ,SAAS;GACT,WAAW,SAAS,QAAQ,KAAK,uBAAuB;EACzD;EACD,WAAW,SAAS,QAAQ,KAAK,uBAAuB;CACzD;AACD,OAAM,WAAW,WAAW,wCAAwC,SAAS;AAC7E,OAAM,WAAW,WAAW,wCAAwC,SAAS;AAC7E,OAAM,WAAW,WAAW,wCAAwC,SAAS;AAC7E,OAAM,WAAW,WAAW,wCAAwC,SAAS;AAE7E,OAAM,EAAE,KAAK,WAAW,YAAY;EAClC,MAAM,SAAS,QAAQ,UAAU,EAAE,OAAO,SAAU,EAAC;EACrD,MAAM,WAAW,MAAM,MAAM,UAAU,OAAO;AAC9C,SAAO,gBAAgB,SAAS,QAAQ,EAAE;AAE1C,SAAO,gBACL,SAAS,GAAG,GAAG,MACf,mEACD;AACD,SAAO,gBACL,SAAS,GAAG,MAAM,IAAI,MACtB,mCACD;AACD,SAAO,gBAAgB,SAAS,GAAG,YAAY,WAAW;AAC1D,SAAO,gBAAgB,SAAS,GAAG,MAAM,gBAAgB;AACzD,SAAO,gBACL,SAAS,GAAG,WACZ,SAAS,QAAQ,KAAK,uBAAuB,CAC9C;AAED,SAAO,gBACL,SAAS,GAAG,GAAG,MACf,mEACD;AACD,SAAO,gBACL,SAAS,GAAG,MAAM,IAAI,MACtB,mCACD;AACD,SAAO,gBAAgB,SAAS,GAAG,YAAY,WAAW;AAC1D,SAAO,gBAAgB,SAAS,GAAG,MAAM,gBAAgB;AACzD,SAAO,gBACL,SAAS,GAAG,WACZ,SAAS,QAAQ,KAAK,uBAAuB,CAC9C;AAED,SAAO,gBACL,SAAS,GAAG,GAAG,MACf,mEACD;AACD,SAAO,gBACL,SAAS,GAAG,MAAM,IAAI,MACtB,mCACD;AACD,SAAO,gBAAgB,SAAS,GAAG,YAAY,WAAW;AAC1D,SAAO,gBAAgB,SAAS,GAAG,MAAM,gBAAgB;AACzD,SAAO,gBACL,SAAS,GAAG,WACZ,SAAS,QAAQ,KAAK,uBAAuB,CAC9C;AAED,SAAO,gBACL,SAAS,GAAG,GAAG,MACf,mEACD;AACD,SAAO,gBACL,SAAS,GAAG,MAAM,IAAI,MACtB,mCACD;AACD,SAAO,gBAAgB,SAAS,GAAG,YAAY,WAAW;AAC1D,SAAO,gBAAgB,SAAS,GAAG,MAAM,gBAAgB;AACzD,SAAO,gBACL,SAAS,GAAG,WACZ,SAAS,QAAQ,KAAK,uBAAuB,CAC9C;CACF,EAAC;AAEF,OAAM,EAAE,KAAK,mBAAmB,YAAY;EAC1C,MAAM,SAAS,QAAQ,UAAU,EAAE,OAAO,SAAU,EAAC;EACrD,MAAM,WAAW,MAAM,MAAM,UAAU,OAAO;EAC9C,MAAM,aAAa,SAAS,IAAI,CAAC,QAAQ,IAAI,GAAG,KAAK;AACrD,SAAO,gBAAgB,YAAY;GACjC;GACA;GACA;GACA;EACD,EAAC;CACH,EAAC;AAEF,OAAM,EAAE,KAAK,mBAAmB,YAAY;EAC1C,MAAM,SAAS,QAAQ,UAAU,EAAE,OAAO,SAAU,EAAC;EACrD,MAAM,WAAW,MAAM,MAAM,UAAU,OAAO;EAC9C,MAAM,aAAa,SAAS,IAAI,CAAC,QAAQ,IAAI,GAAG,KAAK;AACrD,SAAO,gBAAgB,YAAY;GACjC;GACA;GACA;GACA;EACD,EAAC;CACH,EAAC;AAEF,OAAM,EAAE,KAAK,SAAS,YAAY;EAChC,MAAM,SAAS,QAAQ,UAAU,EAC/B,OAAO,SAAS,QAAQ,KAAK,uBAAuB,CACrD,EAAC;EACF,MAAM,WAAW,MAAM,MAAM,UAAU,OAAO;EAC9C,MAAM,aAAa,SAAS,IAAI,CAAC,QAAQ,IAAI,GAAG,KAAK;AACrD,SAAO,gBAAgB,YAAY,CACjC,oEACA,kEACD,EAAC;CACH,EAAC;AAEF,OAAM,EAAE,KAAK,SAAS,YAAY;EAChC,MAAM,SAAS,QAAQ,UAAU,EAC/B,OAAO,SAAS,QAAQ,KAAK,uBAAuB,CACrD,EAAC;EACF,MAAM,WAAW,MAAM,MAAM,UAAU,OAAO;EAC9C,MAAM,aAAa,SAAS,IAAI,CAAC,QAAQ,IAAI,GAAG,KAAK;AACrD,SAAO,gBAAgB,YAAY,CACjC,oEACA,kEACD,EAAC;CACH,EAAC;AAEF,OAAM,EAAE,KAAK,kDAAkD,YAAY;EACzE,MAAM,SAAS,QAAQ,UAAU,EAAE,OAAO,SAAU,EAAC;EACrD,MAAM,WAAW,MAAM,MAAM,UAAU,OAAO;AAC9C,SAAO,YAAY,SAAS,QAAQ,EAAE;AAEtC,OAAK,MAAM,WAAW,UAAU;AAC9B,UAAO,mBAAmB,QAAQ,QAAQ,WAAW;AACrD,UAAO,mBAAmB,QAAQ,QAAQ,WAAW;EACtD;CACF,EAAC;AACH,EAAC;AAWF,SAAgB,kBACdA,KACAC,QACa;CACb,MAAM,MAAM,IAAI,WAAW,cACzB,IAAI,IAAI,gBAET;AACD,KAAI,iBAAiB,CAAE;AACvB,KAAI,eAAe,CAAC,GAAG,YAAY,aAAa;AAC9C,MAAI,eAAe,KAAK;GACtB,YAAY,eAAe,cACvB,cACA,MAAM,QAAQ,WAAW,GACzB,aACA,CAAC,UAAW;GAChB;EACD,EAAC;AACF,SAAO,QAAQ,SAAS;CACzB;AACD,QAAO;AACR"}
|
package/dist/text.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.js","names":["value: unknown","session: Session<TContextData>","text: Text<\"block\" | \"inline\", TContextData>","actor: Actor | URL","strings: TemplateStringsArray","#strings","#values","paraState: \"opened\" | \"closed\"","objects: Object[]","text: string","text","_session: Session<TContextData>","label: string | ((session: Session<TContextData>) => Promise<string>)","actor: Actor | ((session: Session<TContextData>) => Promise<Object | null>)","#label","#actor","#cachedObject","#labelPromise","#actorPromise","#getLabel","#getActor","a: string | Actor | URL","b?: Actor | URL","tag: string","#tag","text: Text<\"inline\", TContextData> | string","#text","label: Text<\"inline\", TContextData> | string","href: URL | string","#href","label: Text<\"inline\", TContextData> | string | URL","href?: URL | string","#code","code: Text<\"inline\", TContextData> | string","code","#emoji","emoji: Emoji | DeferredCustomEmoji<TContextData>","#content","#markdownIt","#mentions","#hashtags","content: string","options: MarkdownTextOptions","mentionPlugin","handle: string","env: MarkdownEnv","env","_handle: string","_env: MarkdownEnv","hashtagPlugin","hashtag: string","_hashtag: string","#getMentionedActors","#actors","actors: Record<string, Object>","actors: Record<string, string | null>","hashtag"],"sources":["../src/text.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 {\n type Actor,\n Emoji,\n getActorHandle,\n Hashtag,\n isActor,\n Link,\n Mention,\n type Object,\n} from \"@fedify/vocab\";\nimport { hashtag as hashtagPlugin } from \"@fedify/markdown-it-hashtag\";\nimport {\n mention as mentionPlugin,\n toFullHandle,\n} from \"@fedify/markdown-it-mention\";\nimport { encode } from \"html-entities\";\nimport MarkdownIt from \"markdown-it\";\nimport type { DeferredCustomEmoji } from \"./emoji.ts\";\nimport type { Session } from \"./session.ts\";\n\n/**\n * A tree structure representing a text with formatting. It does not only\n * render the text but also extract tags (e.g., mentions) from it.\n * @typeParam TContextData The type of the context data.\n */\nexport interface Text<TType extends \"block\" | \"inline\", TContextData> {\n /**\n * The type of the text. It can be either `\"block\"` or `\"inline\"`.\n */\n readonly type: TType;\n\n /**\n * Render a text tree as HTML.\n * @param session The bot session.\n * @returns An async iterable of HTML chunks.\n */\n getHtml(session: Session<TContextData>): AsyncIterable<string>;\n\n /**\n * Extract tags (e.g., mentions) from a text tree.\n * @param session The bot session\n * @returns An async iterable of tags.\n */\n getTags(session: Session<TContextData>): AsyncIterable<Link | Object>;\n\n /**\n * Gets cached objects. The result of this method depends on\n * whether {@link getHtml} or {@link getTags} has been called before.\n * It's used for optimizing the post rendering process, e.g., reusing\n * once fetched remote objects.\n * @returns The cached objects. The order of the objects does not matter.\n */\n getCachedObjects(): Object[];\n}\n\n/**\n * Checks if a value is a {@link Text} tree.\n * @param value The value to check.\n * @returns `true` if the value is a {@link Text} tree, `false` otherwise.\n * @typeParam TContextData The type of the context data.\n */\nexport function isText<TContextData>(\n value: unknown,\n): value is Text<\"block\" | \"inline\", TContextData> {\n return typeof value === \"object\" && value !== null && \"getHtml\" in value &&\n \"getTags\" in value && typeof value.getHtml === \"function\" &&\n typeof value.getTags === \"function\" && \"type\" in value &&\n (value.type === \"block\" || value.type === \"inline\");\n}\n\n/**\n * Checks if a given `actor` is mentioned in a `text`.\n * @param session The bot session.\n * @param text The text object to check.\n * @param actor The actor to check. It can be either an `Actor` object or\n * an actor URI.\n * @returns `true` if the actor is mentioned in the text, `false` otherwise.\n */\nexport async function mentions<TContextData>(\n session: Session<TContextData>,\n text: Text<\"block\" | \"inline\", TContextData>,\n actor: Actor | URL,\n): Promise<boolean> {\n if (isActor(actor)) {\n if (actor.id == null) return false;\n actor = actor.id;\n }\n for await (const tag of text.getTags(session)) {\n if (tag instanceof Mention && tag.href?.href === actor.href) return true;\n }\n return false;\n}\n\n/**\n * A text tree that renders a template string with values. You normally\n * don't need to instantiate this directly; use the {@link text} function\n * instead.\n * @typeParam TContextData The type of the context data.\n */\nexport class TemplatedText<TContextData>\n implements Text<\"block\", TContextData> {\n readonly type = \"block\";\n #strings: TemplateStringsArray;\n #values: Text<\"block\" | \"inline\", TContextData>[];\n\n /**\n * Creates a text tree with a template string and values.\n * @param strings The template strings.\n * @param values The values to interpolate.\n */\n constructor(strings: TemplateStringsArray, ...values: unknown[]) {\n this.#strings = strings;\n this.#values = values.map((v) => {\n if (isText<TContextData>(v)) return v;\n if (v instanceof URL) return link(v);\n if (isActor(v)) return mention(v);\n if (v instanceof Emoji) return customEmoji(v);\n return new PlainText(String(v));\n });\n }\n\n async *getHtml(session: Session<TContextData>): AsyncIterable<string> {\n let paraState: \"opened\" | \"closed\" = \"closed\";\n for (let i = 0; i < this.#strings.length; i++) {\n const paragraphs = this.#strings[i].split(/([ \\t]*\\r?\\n){2,}/g);\n let p = 0;\n for (const para of paragraphs) {\n if (p > 0 && paraState === \"opened\") {\n yield \"</p>\";\n paraState = \"closed\";\n }\n const lines = para.split(\"\\n\");\n let l = 0;\n for (const line of lines) {\n if (line.trim() === \"\") continue;\n if (l < 1 && paraState === \"closed\") {\n yield \"<p>\";\n paraState = \"opened\";\n }\n if (l > 0) yield \"<br>\";\n yield encode(line);\n l++;\n }\n p++;\n }\n if (i < this.#values.length) {\n const value = this.#values[i];\n if (value.type === \"block\" && paraState === \"opened\") {\n yield \"</p>\";\n paraState = \"closed\";\n } else if (value.type === \"inline\" && paraState === \"closed\") {\n yield \"<p>\";\n paraState = \"opened\";\n }\n yield* value.getHtml(session);\n }\n }\n if (paraState === \"opened\") yield \"</p>\";\n }\n\n async *getTags(session: Session<TContextData>): AsyncIterable<Link | Object> {\n for (const value of this.#values) {\n if (!isText<TContextData>(value)) continue;\n yield* value.getTags(session);\n }\n }\n\n getCachedObjects(): Object[] {\n const objects: Object[] = [];\n for (const value of this.#values) {\n if (!isText<TContextData>(value)) continue;\n objects.push(...value.getCachedObjects());\n }\n return objects;\n }\n}\n\n/**\n * A template string tag that creates a {@link Text} tree.\n *\n * Basically, it only interpolates values into the template string and\n * escapes HTML characters, except for line breaks and paragraphs.\n * For example, the below code:\n *\n * ```ts\n * text`Hello, <${em(\"World\")}>!\\n\\nGoodbye!`\n * ```\n *\n * will be rendered as:\n *\n * ```html\n * <p>Hello, <<em>World</em>>!</p>\n * <p>Goodbye!</p>\n * ```\n *\n * @typeParam TContextData The type of the context data.\n * @param strings The template strings.\n * @param values The values to interpolate.\n * @returns A {@link Text} tree.\n */\nexport function text<TContextData>(\n strings: TemplateStringsArray,\n ...values: unknown[]\n): Text<\"block\", TContextData> {\n return new TemplatedText<TContextData>(strings, ...values);\n}\n\n/**\n * A text tree that renders a plain text. You normally don't need to\n * instantiate this directly; use the {@link plainText} function instead.\n * @typeParam TContextData The type of the context data.\n */\nexport class PlainText<TContextData> implements Text<\"inline\", TContextData> {\n readonly type = \"inline\";\n readonly text: string;\n\n /**\n * Creates a {@link PlainText} tree with a plain text.\n * @param text The plain text.\n */\n constructor(text: string) {\n this.text = text;\n }\n\n async *getHtml(_session: Session<TContextData>): AsyncIterable<string> {\n let first = true;\n for (const line of this.text.split(\"\\n\")) {\n if (!first) yield \"<br>\";\n yield encode(line);\n first = false;\n }\n }\n\n async *getTags(\n _session: Session<TContextData>,\n ): AsyncIterable<Link | Object> {\n }\n\n getCachedObjects(): Object[] {\n return [];\n }\n}\n\n/**\n * A function that creates a {@link PlainText} tree. It only does two simple\n * things:\n *\n * - Escaping the given text so that it can be safely rendered as HTML\n * - Splitting the text by line breaks and rendering them as hard line breaks\n * @typeParam TContextData The type of the context data.\n * @param text The plain text.\n * @returns A {@link PlainText} tree.\n */\nexport function plainText<TContextData>(\n text: string,\n): Text<\"inline\", TContextData> {\n return new PlainText(text);\n}\n\n/**\n * A text tree that renders a mention. You normally don't need to\n * instantiate this directly; use the {@link mention} function instead.\n * @typeParam TContextData The type of the context data.\n */\nexport class MentionText<TContextData> implements Text<\"inline\", TContextData> {\n readonly type = \"inline\";\n #label: string | ((session: Session<TContextData>) => Promise<string>);\n #actor: Actor | ((session: Session<TContextData>) => Promise<Object | null>);\n #cachedObject?: Object;\n #labelPromise?: Promise<string>;\n #actorPromise?: Promise<Object | null>;\n\n /**\n * Creates a {@link MentionText} tree with a label and an actor.\n * @param label The label of the mention.\n * @param actor The actor which the mention refers to.\n */\n constructor(\n label: string | ((session: Session<TContextData>) => Promise<string>),\n actor: Actor | ((session: Session<TContextData>) => Promise<Object | null>),\n ) {\n this.#label = label;\n this.#actor = actor;\n if (isActor(actor)) this.#cachedObject = actor;\n }\n\n #getLabel(session: Session<TContextData>): Promise<string> {\n if (typeof this.#label === \"string\") return Promise.resolve(this.#label);\n if (this.#labelPromise != null) return this.#labelPromise;\n return this.#labelPromise = this.#label(session);\n }\n\n #getActor(session: Session<TContextData>): Promise<Object | null> {\n if (isActor(this.#actor)) return Promise.resolve(this.#actor);\n if (this.#actorPromise != null) return this.#actorPromise;\n return this.#actorPromise = this.#actor(session).then((actor) => {\n if (actor != null) this.#cachedObject = actor;\n return actor;\n });\n }\n\n async *getHtml(session: Session<TContextData>): AsyncIterable<string> {\n const label = await this.#getLabel(session);\n const actor = await this.#getActor(session);\n const url = !isActor(actor)\n ? null\n : actor.url == null\n ? actor.id\n : actor.url instanceof Link\n ? actor.url.href\n : actor.url;\n if (url == null) {\n yield encode(label);\n return;\n }\n yield '<a href=\"';\n yield encode(url.href);\n yield '\" translate=\"no\" class=\"h-card u-url mention\" target=\"_blank\">';\n if (label.startsWith(\"@\")) {\n yield \"@<span>\";\n yield encode(label.substring(1));\n yield \"</span>\";\n } else {\n yield encode(label);\n }\n yield \"</a>\";\n }\n\n async *getTags(session: Session<TContextData>): AsyncIterable<Link | Object> {\n const label = await this.#getLabel(session);\n const actor = await this.#getActor(session);\n if (isActor(actor)) {\n yield new Mention({\n name: label,\n href: actor.id,\n });\n }\n }\n\n getCachedObjects(): Object[] {\n return this.#cachedObject == null ? [] : [this.#cachedObject];\n }\n}\n\n/**\n * Mentions an actor by its fediverse handle. You can use this function\n * to create a {@link MentionText} tree. The label of the mention will be\n * the same as the handle.\n *\n * If the given handle does not refer to an actor, the returned tree consists\n * of a plain text with the handle without any link.\n * @typeParam TContextData The type of the context data.\n * @param handle The handle of the actor.\n * @returns A {@link MentionText} tree.\n */\nexport function mention<TContextData>(\n handle: string,\n): Text<\"inline\", TContextData>;\n\n/**\n * Mentions an actor. You can use this function to create a {@link MentionText}\n * from an actor object. The label of the mention will be the fediverse handle\n * of the actor.\n * @typeParam TContextData The type of the context data.\n * @param actor The actor to mention.\n * @returns A {@link MentionText} tree.\n */\nexport function mention<TContextData>(\n actor: Actor | URL,\n): Text<\"inline\", TContextData>;\n\n/**\n * Mentions an actor with a custom label. You can use this function to create\n * a {@link MentionText} tree from an actor object with a custom label.\n *\n * If the given actor is a URL and the URL does not refer to an actor,\n * the returned tree consists of a plain text with the URL without any link.\n * @typeParam TContextData The type of the context data.\n * @param label The label of the mention.\n * @param actor The actor to mention.\n */\nexport function mention<TContextData>(\n label: string,\n actor: Actor | URL,\n): Text<\"inline\", TContextData>;\n\nexport function mention<TContextData>(\n a: string | Actor | URL,\n b?: Actor | URL,\n): Text<\"inline\", TContextData> {\n if (b != null) {\n // (label: string, actor: Actor | URL)\n return new MentionText<TContextData>(\n a as string,\n isActor(b) ? b : async (session) => {\n if (session.actorId.href === b.href) return await session.getActor();\n const documentLoader = await session.context.getDocumentLoader(\n session.bot,\n );\n return await session.context.lookupObject(b, { documentLoader });\n },\n );\n } else if (typeof a === \"string\") {\n // (handle: string)\n return new MentionText<TContextData>(\n a,\n async (session) => {\n if (session.actorHandle === a) return await session.getActor();\n const documentLoader = await session.context.getDocumentLoader(\n session.bot,\n );\n return await session.context.lookupObject(a, { documentLoader });\n },\n );\n } else if (isActor(a)) {\n // (actor: Actor)\n return new MentionText<TContextData>(\n (session) =>\n a.id?.href === session.actorId.href\n ? Promise.resolve(session.actorHandle)\n : getActorHandle(a, session.context),\n a,\n );\n }\n // (actor: URL)\n return new MentionText<TContextData>(\n (session) =>\n a.href === session.actorId.href\n ? Promise.resolve(session.actorHandle)\n : getActorHandle(a, session.context),\n async (session) => {\n if (a.href === session.actorId.href) return await session.getActor();\n const documentLoader = await session.context.getDocumentLoader(\n session.bot,\n );\n return await session.context.lookupObject(a, { documentLoader });\n },\n );\n}\n\n/**\n * A text tree that renders a hashtag. You normally don't need to\n * instantiate this directly; use the {@link hashtag} function instead.\n * @typeParam TContextData The type of the context data.\n */\nexport class HashtagText<TContextData> implements Text<\"inline\", TContextData> {\n readonly type = \"inline\";\n #tag: string;\n\n /**\n * Creates a {@link HashtagText} tree with a tag.\n * @param tag The hashtag. It does not matter whether it starts with `\"#\"`.\n */\n constructor(tag: string) {\n this.#tag = tag.trimStart().replace(/^#/, \"\").trim().replace(/\\s+/g, \" \");\n }\n\n async *getHtml(session: Session<TContextData>): AsyncIterable<string> {\n yield '<a href=\"';\n yield encode(session.context.origin);\n yield \"/tags/\";\n yield encode(encodeURIComponent(this.#tag.toLowerCase()));\n yield '\" class=\"mention hashtag\" rel=\"tag\" target=\"_blank\">#<span>';\n yield this.#tag;\n yield \"</span></a>\";\n }\n\n async *getTags(session: Session<TContextData>): AsyncIterable<Link | Object> {\n yield new Hashtag({\n href: new URL(\n `/tags/${encodeURIComponent(this.#tag.toLowerCase())}`,\n session.context.origin,\n ),\n name: `#${this.#tag.toLowerCase()}`,\n });\n }\n\n getCachedObjects(): Object[] {\n return [];\n }\n}\n\n/**\n * Creates a hashtag. You can use this function to create a {@link HashtagText}\n * tree.\n * @param tag The hashtag. It does not matter whether it starts with `\"#\"`.\n * @returns A {@link HashtagText} tree.\n */\nexport function hashtag<TContextData>(\n tag: string,\n): Text<\"inline\", TContextData> {\n return new HashtagText(tag);\n}\n\n/**\n * A text tree that renders a `<strong>` text. You normally don't need to\n * instantiate this directly; use the {@link strong} function instead.\n * @typeParam TContextData The type of the context data.\n */\nexport class StrongText<TContextData> implements Text<\"inline\", TContextData> {\n readonly type = \"inline\";\n #text: Text<\"inline\", TContextData>;\n\n /**\n * Creates a {@link StrongText} tree with a text.\n * @param text The text to render as `<strong>`.\n */\n constructor(text: Text<\"inline\", TContextData> | string) {\n this.#text = typeof text === \"string\" ? new PlainText(text) : text;\n }\n\n async *getHtml(session: Session<TContextData>): AsyncIterable<string> {\n yield \"<strong>\";\n yield* this.#text.getHtml(session);\n yield \"</strong>\";\n }\n\n getTags(session: Session<TContextData>): AsyncIterable<Link | Object> {\n return this.#text.getTags(session);\n }\n\n getCachedObjects(): Object[] {\n return [];\n }\n}\n\n/**\n * Applies `<strong>` tag to a text. You can use this function to create a\n * {@link StrongText} tree.\n * @typeParam TContextData The type of the context data.\n * @param text The text to render as `<strong>`. It can be a plain text or\n * another text tree.\n * @returns A {@link StrongText} tree.\n */\nexport function strong<TContextData>(\n text: Text<\"inline\", TContextData> | string,\n): Text<\"inline\", TContextData> {\n return new StrongText(text);\n}\n\n/**\n * A text tree that renders an `<em>` text. You normally don't need to\n * instantiate this directly; use the {@link em} function instead.\n * @typeParam TContextData The type of the context data.\n */\nexport class EmText<TContextData> implements Text<\"inline\", TContextData> {\n readonly type = \"inline\";\n #text: Text<\"inline\", TContextData>;\n\n constructor(text: Text<\"inline\", TContextData> | string) {\n this.#text = typeof text === \"string\" ? new PlainText(text) : text;\n }\n\n async *getHtml(session: Session<TContextData>): AsyncIterable<string> {\n yield \"<em>\";\n yield* this.#text.getHtml(session);\n yield \"</em>\";\n }\n\n getTags(session: Session<TContextData>): AsyncIterable<Link | Object> {\n return this.#text.getTags(session);\n }\n\n getCachedObjects(): Object[] {\n return [];\n }\n}\n\n/**\n * Applies `<em>` tag to a text. You can use this function to create an\n * {@link EmText} tree.\n * @typeParam TContextData The type of the context data.\n * @param text The text to render as `<em>`. It can be a plain text or\n * another text tree.\n * @returns A {@link EmText} tree.\n */\nexport function em<TContextData>(\n text: Text<\"inline\", TContextData> | string,\n): Text<\"inline\", TContextData> {\n return new EmText(text);\n}\n\n/**\n * A text tree that renders a link. You normally don't need to instantiate\n * this directly; use the {@link link} function instead.\n * @typeParam TContextData The type of the context data.\n */\nexport class LinkText<TContextData> implements Text<\"inline\", TContextData> {\n readonly type = \"inline\";\n #label: Text<\"inline\", TContextData>;\n #href: URL;\n\n /**\n * Creates a {@link LinkText} tree with a label and a URL.\n * @param label The label of the link.\n * @param href The URL of the link. It has to be an absolute URL.\n */\n constructor(\n label: Text<\"inline\", TContextData> | string,\n href: URL | string,\n ) {\n this.#label = typeof label === \"string\" ? new PlainText(label) : label;\n this.#href = typeof href === \"string\" ? new URL(href) : href;\n }\n\n async *getHtml(session: Session<TContextData>): AsyncIterable<string> {\n yield '<a href=\"';\n yield encode(this.#href.href);\n yield '\" target=\"_blank\">';\n yield* this.#label.getHtml(session);\n yield \"</a>\";\n }\n\n getTags(session: Session<TContextData>): AsyncIterable<Link | Object> {\n return this.#label.getTags(session);\n }\n\n getCachedObjects(): Object[] {\n return this.#label.getCachedObjects();\n }\n}\n\n/**\n * Creates a link to the given `href` with the `label`. You can use this\n * function to create a {@link LinkText} tree.\n * @typeParam TContextData The type of the context data.\n * @param label The displayed label of the link.\n * @param href The link target. It has to be an absolute URL.\n * @returns A {@link LinkText} tree.\n */\nexport function link<TContextData>(\n label: Text<\"inline\", TContextData> | string,\n href: URL | string,\n): Text<\"inline\", TContextData>;\n\n/**\n * Creates a link to the given `url` with no label. You can use this function\n * to create a {@link LinkText} tree. The label of the link will be the same\n * as the given `url`.\n * @param url The link target. It has to be an absolute URL.\n * @returns A {@link LinkText} tree.\n */\nexport function link<TContextData>(\n url: URL | string,\n): Text<\"inline\", TContextData>;\n\nexport function link<TContextData>(\n label: Text<\"inline\", TContextData> | string | URL,\n href?: URL | string,\n): Text<\"inline\", TContextData> {\n return href == null\n ? new LinkText(String(label), label as string)\n : new LinkText(\n isText<TContextData>(label) ? label : label.toString(),\n href,\n );\n}\n\n/**\n * A text tree that renders a inline code. You normally don't need to\n * instantiate this directly; use the {@link code} function instead.\n * @typeParam TContextData The type of the context data.\n */\nexport class CodeText<TContextData> implements Text<\"inline\", TContextData> {\n readonly type = \"inline\";\n readonly #code: Text<\"inline\", TContextData>;\n\n /**\n * Creates a {@link CodeText} tree with a code.\n * @param code The code to render.\n */\n constructor(code: Text<\"inline\", TContextData> | string) {\n this.#code = typeof code === \"string\" ? new PlainText(code) : code;\n }\n\n async *getHtml(session: Session<TContextData>): AsyncIterable<string> {\n yield \"<code>\";\n yield* this.#code.getHtml(session);\n yield \"</code>\";\n }\n\n getTags(session: Session<TContextData>): AsyncIterable<Link | Object> {\n return this.#code.getTags(session);\n }\n\n getCachedObjects(): Object[] {\n return [];\n }\n}\n\n/**\n * Applies `<code>` tag to a text. You can use this function to create\n * a {@link CodeText} tree.\n * @param code The code to render.\n * @returns A {@link CodeText} tree.\n */\nexport function code<TContextData>(\n code: Text<\"inline\", TContextData> | string,\n): Text<\"inline\", TContextData> {\n return new CodeText(code);\n}\n\n/**\n * A text tree that renders a custom emoji. You normally don't need to\n * instantiate this directly; use the {@link customEmoji} function instead.\n * @typeParam TContextData The type of the context data.\n * @since 0.2.0\n */\nexport class CustomEmojiText<TContextData>\n implements Text<\"inline\", TContextData> {\n readonly type = \"inline\";\n readonly #emoji: Emoji | DeferredCustomEmoji<TContextData>;\n\n /**\n * Creates a {@link CustomEmojiText} tree with a custom emoji.\n * @param emoji The custom emoji to render.\n */\n constructor(emoji: Emoji | DeferredCustomEmoji<TContextData>) {\n this.#emoji = emoji;\n }\n\n /**\n * Gets the emoji object. If the emoji is a deferred emoji, it will\n * be resolved with the given session.\n * @param session The bot session.\n * @returns The emoji object.\n */\n getEmoji(session: Session<TContextData>): Emoji {\n if (typeof this.#emoji === \"function\") return this.#emoji(session);\n return this.#emoji;\n }\n\n async *getHtml(session: Session<TContextData>): AsyncIterable<string> {\n const emoji = this.getEmoji(session);\n if (emoji.name == null) return;\n yield \"\\u200b\"; // zero-width space for segmentation\n yield encode(emoji.name.toString());\n yield \"\\u200b\";\n }\n\n async *getTags(\n session: Session<TContextData>,\n ): AsyncIterable<Link | Object> {\n yield this.getEmoji(session);\n }\n\n getCachedObjects(): Object[] {\n return [];\n }\n}\n\n/**\n * Renders a custom emoji. You can use this function to create a\n * {@link CustomEmojiText} tree.\n * @param emoji The custom emoji to render. See also {@link Bot.addCustomEmojis}\n * method.\n * @returns A {@link CustomEmojiText} tree.\n * @since 0.2.0\n */\nexport function customEmoji<TContextData>(\n emoji: Emoji | DeferredCustomEmoji<TContextData>,\n): Text<\"inline\", TContextData> {\n return new CustomEmojiText(emoji);\n}\n\n/**\n * The options for rendering a Markdown text.\n */\nexport interface MarkdownTextOptions {\n /**\n * Whether to render mentions in the Markdown text.\n * @default {true}\n */\n readonly mentions?: boolean;\n\n /**\n * Whether to render hashtags in the Markdown text.\n * @default {true}\n */\n readonly hashtags?: boolean;\n\n /**\n * Whether to automatically linkify URLs in the Markdown text.\n * @default {true}\n */\n readonly linkify?: boolean;\n}\n\ninterface MarkdownEnv {\n mentions: string[];\n hashtags: string[];\n origin: string;\n actors?: Record<string, string | null>;\n}\n\n/**\n * A text tree that renders a Markdown text. You normally don't need to\n * instantiate this directly; use the {@link markdown} function instead.\n */\nexport class MarkdownText<TContextData> implements Text<\"block\", TContextData> {\n readonly type = \"block\";\n readonly #content: string;\n readonly #markdownIt: MarkdownIt;\n readonly #mentions?: string[];\n readonly #hashtags?: string[];\n #actors?: Record<string, Object>;\n\n /**\n * Creates a {@link MarkdownText} tree with a Markdown content.\n * @param content The Markdown content.\n * @param options The options for rendering the Markdown content.\n */\n constructor(content: string, options: MarkdownTextOptions = {}) {\n this.#content = content;\n const md = MarkdownIt({\n html: false,\n linkify: options.linkify ?? true,\n });\n if (options.mentions ?? true) {\n md.use(mentionPlugin, {\n link(handle: string, env: MarkdownEnv) {\n if (env.actors == null) return `acct:${handle}`;\n return env.actors[handle] ?? null;\n },\n linkAttributes(_handle: string, _env: MarkdownEnv) {\n return {\n translate: \"no\",\n class: \"h-card u-url mention\",\n target: \"_blank\",\n };\n },\n label: toFullHandle,\n });\n const env: MarkdownEnv = {\n mentions: [],\n hashtags: [],\n origin: \"http://localhost\",\n };\n md.render(content, env);\n this.#mentions = env.mentions;\n }\n if (options.hashtags ?? true) {\n md.use(hashtagPlugin, {\n link(hashtag: string, env: MarkdownEnv) {\n const tag = hashtag.substring(1).toLowerCase();\n return new URL(`/tags/${encodeURIComponent(tag)}`, env.origin).href;\n },\n linkAttributes(_hashtag: string, _env: MarkdownEnv) {\n return {\n class: \"mention hashtag\",\n rel: \"tag\",\n target: \"_blank\",\n };\n },\n label(hashtag: string, _env: MarkdownEnv) {\n const tag = hashtag.substring(1);\n return `#<span>${tag}</span>`;\n },\n });\n const env: MarkdownEnv = {\n mentions: [],\n hashtags: [],\n origin: \"http://localhost\",\n };\n md.render(content, env);\n this.#hashtags = env.hashtags;\n }\n this.#markdownIt = md;\n }\n\n async #getMentionedActors(\n session: Session<TContextData>,\n ): Promise<Record<string, Object>> {\n if (this.#mentions == null) return {};\n if (this.#actors != null) return this.#actors;\n const documentLoader = await session.context.getDocumentLoader(session.bot);\n const objects = await Promise.all(\n this.#mentions.map((m) =>\n m === session.actorHandle\n ? session.getActor()\n : session.context.lookupObject(m, { documentLoader })\n ),\n );\n const actors: Record<string, Object> = {};\n for (let i = 0; i < this.#mentions.length; i++) {\n const object = objects[i];\n if (object != null) actors[this.#mentions[i]] = object;\n }\n this.#actors = actors;\n return actors;\n }\n\n async *getHtml(session: Session<TContextData>): AsyncIterable<string> {\n if (this.#mentions == null) {\n yield this.#markdownIt.render(this.#content);\n return;\n }\n const actors: Record<string, string | null> = globalThis.Object.fromEntries(\n globalThis.Object.entries(\n await this.#getMentionedActors(session),\n ).filter(([_, obj]) => isActor(obj)).map((\n [handle, actor],\n ) =>\n [\n handle,\n (actor.url instanceof Link\n ? actor.url.href?.href\n : actor.url?.href) ?? actor.id?.href ?? null,\n ] satisfies [string, string | null]\n ).filter(([_, url]) => url != null),\n );\n const env: MarkdownEnv = {\n mentions: [],\n hashtags: [],\n origin: session.context.origin,\n actors,\n };\n yield this.#markdownIt.render(this.#content, env);\n }\n\n async *getTags(session: Session<TContextData>): AsyncIterable<Link | Object> {\n if (this.#mentions == null) return;\n const actors = await this.#getMentionedActors(session);\n for (const [handle, object] of globalThis.Object.entries(actors)) {\n if (!isActor(object) || object.id == null) continue;\n yield new Mention({\n name: handle,\n href: object.id,\n });\n }\n if (this.#hashtags != null) {\n for (const hashtag of this.#hashtags) {\n const tag = hashtag.substring(1).toLowerCase();\n yield new Hashtag({\n name: `#${tag}`,\n href: new URL(\n `/tags/${encodeURIComponent(tag)}`,\n session.context.origin,\n ),\n });\n }\n }\n }\n\n getCachedObjects(): Object[] {\n return this.#actors == null ? [] : globalThis.Object.values(this.#actors);\n }\n}\n\n/**\n * Renders a Markdown text. You can use this function to create\n * a {@link MarkdownText} tree. The mentions in the Markdown text\n * will be rendered as links unless the `mentions` option is set to\n * `false`.\n * @param content The Markdown content.\n * @param options The options for rendering the Markdown content.\n * @returns A {@link MarkdownText} tree.\n */\nexport function markdown<TContextData>(\n content: string,\n options: MarkdownTextOptions = {},\n): Text<\"block\", TContextData> {\n return new MarkdownText(content, options);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"text.js","names":["value: unknown","session: Session<TContextData>","text: Text<\"block\" | \"inline\", TContextData>","actor: Actor | URL","strings: TemplateStringsArray","#strings","#values","paraState: \"opened\" | \"closed\"","objects: Object[]","text: string","text","_session: Session<TContextData>","label: string | ((session: Session<TContextData>) => Promise<string>)","actor: Actor | ((session: Session<TContextData>) => Promise<Object | null>)","#label","#actor","#cachedObject","#labelPromise","#actorPromise","#getLabel","#getActor","a: string | Actor | URL","b?: Actor | URL","tag: string","#tag","text: Text<\"inline\", TContextData> | string","#text","label: Text<\"inline\", TContextData> | string","href: URL | string","#href","label: Text<\"inline\", TContextData> | string | URL","href?: URL | string","#code","code: Text<\"inline\", TContextData> | string","code","#emoji","emoji: Emoji | DeferredCustomEmoji<TContextData>","#content","#markdownIt","#mentions","#hashtags","content: string","options: MarkdownTextOptions","mentionPlugin","handle: string","env: MarkdownEnv","env","_handle: string","_env: MarkdownEnv","hashtagPlugin","hashtag: string","_hashtag: string","#getMentionedActors","#actors","actors: Record<string, Object>","actors: Record<string, string | null>","hashtag"],"sources":["../src/text.ts"],"sourcesContent":["// BotKit by Fedify: A framework for creating ActivityPub bots\n// Copyright (C) 2025–2026 Hong Minhee <https://hongminhee.org/>\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\nimport {\n type Actor,\n Emoji,\n getActorHandle,\n Hashtag,\n isActor,\n Link,\n Mention,\n type Object,\n} from \"@fedify/vocab\";\nimport { hashtag as hashtagPlugin } from \"@fedify/markdown-it-hashtag\";\nimport {\n mention as mentionPlugin,\n toFullHandle,\n} from \"@fedify/markdown-it-mention\";\nimport { encode } from \"html-entities\";\nimport MarkdownIt from \"markdown-it\";\nimport type { DeferredCustomEmoji } from \"./emoji.ts\";\nimport type { Session } from \"./session.ts\";\n\n/**\n * A tree structure representing a text with formatting. It does not only\n * render the text but also extract tags (e.g., mentions) from it.\n * @typeParam TContextData The type of the context data.\n */\nexport interface Text<TType extends \"block\" | \"inline\", TContextData> {\n /**\n * The type of the text. It can be either `\"block\"` or `\"inline\"`.\n */\n readonly type: TType;\n\n /**\n * Render a text tree as HTML.\n * @param session The bot session.\n * @returns An async iterable of HTML chunks.\n */\n getHtml(session: Session<TContextData>): AsyncIterable<string>;\n\n /**\n * Extract tags (e.g., mentions) from a text tree.\n * @param session The bot session\n * @returns An async iterable of tags.\n */\n getTags(session: Session<TContextData>): AsyncIterable<Link | Object>;\n\n /**\n * Gets cached objects. The result of this method depends on\n * whether {@link getHtml} or {@link getTags} has been called before.\n * It's used for optimizing the post rendering process, e.g., reusing\n * once fetched remote objects.\n * @returns The cached objects. The order of the objects does not matter.\n */\n getCachedObjects(): Object[];\n}\n\n/**\n * Checks if a value is a {@link Text} tree.\n * @param value The value to check.\n * @returns `true` if the value is a {@link Text} tree, `false` otherwise.\n * @typeParam TContextData The type of the context data.\n */\nexport function isText<TContextData>(\n value: unknown,\n): value is Text<\"block\" | \"inline\", TContextData> {\n return typeof value === \"object\" && value !== null && \"getHtml\" in value &&\n \"getTags\" in value && typeof value.getHtml === \"function\" &&\n typeof value.getTags === \"function\" && \"type\" in value &&\n (value.type === \"block\" || value.type === \"inline\");\n}\n\n/**\n * Checks if a given `actor` is mentioned in a `text`.\n * @param session The bot session.\n * @param text The text object to check.\n * @param actor The actor to check. It can be either an `Actor` object or\n * an actor URI.\n * @returns `true` if the actor is mentioned in the text, `false` otherwise.\n */\nexport async function mentions<TContextData>(\n session: Session<TContextData>,\n text: Text<\"block\" | \"inline\", TContextData>,\n actor: Actor | URL,\n): Promise<boolean> {\n if (isActor(actor)) {\n if (actor.id == null) return false;\n actor = actor.id;\n }\n for await (const tag of text.getTags(session)) {\n if (tag instanceof Mention && tag.href?.href === actor.href) return true;\n }\n return false;\n}\n\n/**\n * A text tree that renders a template string with values. You normally\n * don't need to instantiate this directly; use the {@link text} function\n * instead.\n * @typeParam TContextData The type of the context data.\n */\nexport class TemplatedText<TContextData>\n implements Text<\"block\", TContextData> {\n readonly type = \"block\";\n #strings: TemplateStringsArray;\n #values: Text<\"block\" | \"inline\", TContextData>[];\n\n /**\n * Creates a text tree with a template string and values.\n * @param strings The template strings.\n * @param values The values to interpolate.\n */\n constructor(strings: TemplateStringsArray, ...values: unknown[]) {\n this.#strings = strings;\n this.#values = values.map((v) => {\n if (isText<TContextData>(v)) return v;\n if (v instanceof URL) return link(v);\n if (isActor(v)) return mention(v);\n if (v instanceof Emoji) return customEmoji(v);\n return new PlainText(String(v));\n });\n }\n\n async *getHtml(session: Session<TContextData>): AsyncIterable<string> {\n let paraState: \"opened\" | \"closed\" = \"closed\";\n for (let i = 0; i < this.#strings.length; i++) {\n const paragraphs = this.#strings[i].split(/([ \\t]*\\r?\\n){2,}/g);\n let p = 0;\n for (const para of paragraphs) {\n if (p > 0 && paraState === \"opened\") {\n yield \"</p>\";\n paraState = \"closed\";\n }\n const lines = para.split(\"\\n\");\n let l = 0;\n for (const line of lines) {\n if (line.trim() === \"\") continue;\n if (l < 1 && paraState === \"closed\") {\n yield \"<p>\";\n paraState = \"opened\";\n }\n if (l > 0) yield \"<br>\";\n yield encode(line);\n l++;\n }\n p++;\n }\n if (i < this.#values.length) {\n const value = this.#values[i];\n if (value.type === \"block\" && paraState === \"opened\") {\n yield \"</p>\";\n paraState = \"closed\";\n } else if (value.type === \"inline\" && paraState === \"closed\") {\n yield \"<p>\";\n paraState = \"opened\";\n }\n yield* value.getHtml(session);\n }\n }\n if (paraState === \"opened\") yield \"</p>\";\n }\n\n async *getTags(session: Session<TContextData>): AsyncIterable<Link | Object> {\n for (const value of this.#values) {\n if (!isText<TContextData>(value)) continue;\n yield* value.getTags(session);\n }\n }\n\n getCachedObjects(): Object[] {\n const objects: Object[] = [];\n for (const value of this.#values) {\n if (!isText<TContextData>(value)) continue;\n objects.push(...value.getCachedObjects());\n }\n return objects;\n }\n}\n\n/**\n * A template string tag that creates a {@link Text} tree.\n *\n * Basically, it only interpolates values into the template string and\n * escapes HTML characters, except for line breaks and paragraphs.\n * For example, the below code:\n *\n * ```ts\n * text`Hello, <${em(\"World\")}>!\\n\\nGoodbye!`\n * ```\n *\n * will be rendered as:\n *\n * ```html\n * <p>Hello, <<em>World</em>>!</p>\n * <p>Goodbye!</p>\n * ```\n *\n * @typeParam TContextData The type of the context data.\n * @param strings The template strings.\n * @param values The values to interpolate.\n * @returns A {@link Text} tree.\n */\nexport function text<TContextData>(\n strings: TemplateStringsArray,\n ...values: unknown[]\n): Text<\"block\", TContextData> {\n return new TemplatedText<TContextData>(strings, ...values);\n}\n\n/**\n * A text tree that renders a plain text. You normally don't need to\n * instantiate this directly; use the {@link plainText} function instead.\n * @typeParam TContextData The type of the context data.\n */\nexport class PlainText<TContextData> implements Text<\"inline\", TContextData> {\n readonly type = \"inline\";\n readonly text: string;\n\n /**\n * Creates a {@link PlainText} tree with a plain text.\n * @param text The plain text.\n */\n constructor(text: string) {\n this.text = text;\n }\n\n async *getHtml(_session: Session<TContextData>): AsyncIterable<string> {\n let first = true;\n for (const line of this.text.split(\"\\n\")) {\n if (!first) yield \"<br>\";\n yield encode(line);\n first = false;\n }\n }\n\n async *getTags(\n _session: Session<TContextData>,\n ): AsyncIterable<Link | Object> {\n }\n\n getCachedObjects(): Object[] {\n return [];\n }\n}\n\n/**\n * A function that creates a {@link PlainText} tree. It only does two simple\n * things:\n *\n * - Escaping the given text so that it can be safely rendered as HTML\n * - Splitting the text by line breaks and rendering them as hard line breaks\n * @typeParam TContextData The type of the context data.\n * @param text The plain text.\n * @returns A {@link PlainText} tree.\n */\nexport function plainText<TContextData>(\n text: string,\n): Text<\"inline\", TContextData> {\n return new PlainText(text);\n}\n\n/**\n * A text tree that renders a mention. You normally don't need to\n * instantiate this directly; use the {@link mention} function instead.\n * @typeParam TContextData The type of the context data.\n */\nexport class MentionText<TContextData> implements Text<\"inline\", TContextData> {\n readonly type = \"inline\";\n #label: string | ((session: Session<TContextData>) => Promise<string>);\n #actor: Actor | ((session: Session<TContextData>) => Promise<Object | null>);\n #cachedObject?: Object;\n #labelPromise?: Promise<string>;\n #actorPromise?: Promise<Object | null>;\n\n /**\n * Creates a {@link MentionText} tree with a label and an actor.\n * @param label The label of the mention.\n * @param actor The actor which the mention refers to.\n */\n constructor(\n label: string | ((session: Session<TContextData>) => Promise<string>),\n actor: Actor | ((session: Session<TContextData>) => Promise<Object | null>),\n ) {\n this.#label = label;\n this.#actor = actor;\n if (isActor(actor)) this.#cachedObject = actor;\n }\n\n #getLabel(session: Session<TContextData>): Promise<string> {\n if (typeof this.#label === \"string\") return Promise.resolve(this.#label);\n if (this.#labelPromise != null) return this.#labelPromise;\n return this.#labelPromise = this.#label(session);\n }\n\n #getActor(session: Session<TContextData>): Promise<Object | null> {\n if (isActor(this.#actor)) return Promise.resolve(this.#actor);\n if (this.#actorPromise != null) return this.#actorPromise;\n return this.#actorPromise = this.#actor(session).then((actor) => {\n if (actor != null) this.#cachedObject = actor;\n return actor;\n });\n }\n\n async *getHtml(session: Session<TContextData>): AsyncIterable<string> {\n const label = await this.#getLabel(session);\n const actor = await this.#getActor(session);\n const url = !isActor(actor)\n ? null\n : actor.url == null\n ? actor.id\n : actor.url instanceof Link\n ? actor.url.href\n : actor.url;\n if (url == null) {\n yield encode(label);\n return;\n }\n yield '<a href=\"';\n yield encode(url.href);\n yield '\" translate=\"no\" class=\"h-card u-url mention\" target=\"_blank\">';\n if (label.startsWith(\"@\")) {\n yield \"@<span>\";\n yield encode(label.substring(1));\n yield \"</span>\";\n } else {\n yield encode(label);\n }\n yield \"</a>\";\n }\n\n async *getTags(session: Session<TContextData>): AsyncIterable<Link | Object> {\n const label = await this.#getLabel(session);\n const actor = await this.#getActor(session);\n if (isActor(actor)) {\n yield new Mention({\n name: label,\n href: actor.id,\n });\n }\n }\n\n getCachedObjects(): Object[] {\n return this.#cachedObject == null ? [] : [this.#cachedObject];\n }\n}\n\n/**\n * Mentions an actor by its fediverse handle. You can use this function\n * to create a {@link MentionText} tree. The label of the mention will be\n * the same as the handle.\n *\n * If the given handle does not refer to an actor, the returned tree consists\n * of a plain text with the handle without any link.\n * @typeParam TContextData The type of the context data.\n * @param handle The handle of the actor.\n * @returns A {@link MentionText} tree.\n */\nexport function mention<TContextData>(\n handle: string,\n): Text<\"inline\", TContextData>;\n\n/**\n * Mentions an actor. You can use this function to create a {@link MentionText}\n * from an actor object. The label of the mention will be the fediverse handle\n * of the actor.\n * @typeParam TContextData The type of the context data.\n * @param actor The actor to mention.\n * @returns A {@link MentionText} tree.\n */\nexport function mention<TContextData>(\n actor: Actor | URL,\n): Text<\"inline\", TContextData>;\n\n/**\n * Mentions an actor with a custom label. You can use this function to create\n * a {@link MentionText} tree from an actor object with a custom label.\n *\n * If the given actor is a URL and the URL does not refer to an actor,\n * the returned tree consists of a plain text with the URL without any link.\n * @typeParam TContextData The type of the context data.\n * @param label The label of the mention.\n * @param actor The actor to mention.\n */\nexport function mention<TContextData>(\n label: string,\n actor: Actor | URL,\n): Text<\"inline\", TContextData>;\n\nexport function mention<TContextData>(\n a: string | Actor | URL,\n b?: Actor | URL,\n): Text<\"inline\", TContextData> {\n if (b != null) {\n // (label: string, actor: Actor | URL)\n return new MentionText<TContextData>(\n a as string,\n isActor(b) ? b : async (session) => {\n if (session.actorId.href === b.href) return await session.getActor();\n const documentLoader = await session.context.getDocumentLoader(\n session.bot,\n );\n return await session.context.lookupObject(b, { documentLoader });\n },\n );\n } else if (typeof a === \"string\") {\n // (handle: string)\n return new MentionText<TContextData>(\n a,\n async (session) => {\n if (session.actorHandle === a) return await session.getActor();\n const documentLoader = await session.context.getDocumentLoader(\n session.bot,\n );\n return await session.context.lookupObject(a, { documentLoader });\n },\n );\n } else if (isActor(a)) {\n // (actor: Actor)\n return new MentionText<TContextData>(\n (session) =>\n a.id?.href === session.actorId.href\n ? Promise.resolve(session.actorHandle)\n : getActorHandle(a, session.context),\n a,\n );\n }\n // (actor: URL)\n return new MentionText<TContextData>(\n (session) =>\n a.href === session.actorId.href\n ? Promise.resolve(session.actorHandle)\n : getActorHandle(a, session.context),\n async (session) => {\n if (a.href === session.actorId.href) return await session.getActor();\n const documentLoader = await session.context.getDocumentLoader(\n session.bot,\n );\n return await session.context.lookupObject(a, { documentLoader });\n },\n );\n}\n\n/**\n * A text tree that renders a hashtag. You normally don't need to\n * instantiate this directly; use the {@link hashtag} function instead.\n * @typeParam TContextData The type of the context data.\n */\nexport class HashtagText<TContextData> implements Text<\"inline\", TContextData> {\n readonly type = \"inline\";\n #tag: string;\n\n /**\n * Creates a {@link HashtagText} tree with a tag.\n * @param tag The hashtag. It does not matter whether it starts with `\"#\"`.\n */\n constructor(tag: string) {\n this.#tag = tag.trimStart().replace(/^#/, \"\").trim().replace(/\\s+/g, \" \");\n }\n\n async *getHtml(session: Session<TContextData>): AsyncIterable<string> {\n yield '<a href=\"';\n yield encode(session.context.origin);\n yield \"/tags/\";\n yield encode(encodeURIComponent(this.#tag.toLowerCase()));\n yield '\" class=\"mention hashtag\" rel=\"tag\" target=\"_blank\">#<span>';\n yield this.#tag;\n yield \"</span></a>\";\n }\n\n async *getTags(session: Session<TContextData>): AsyncIterable<Link | Object> {\n yield new Hashtag({\n href: new URL(\n `/tags/${encodeURIComponent(this.#tag.toLowerCase())}`,\n session.context.origin,\n ),\n name: `#${this.#tag.toLowerCase()}`,\n });\n }\n\n getCachedObjects(): Object[] {\n return [];\n }\n}\n\n/**\n * Creates a hashtag. You can use this function to create a {@link HashtagText}\n * tree.\n * @param tag The hashtag. It does not matter whether it starts with `\"#\"`.\n * @returns A {@link HashtagText} tree.\n */\nexport function hashtag<TContextData>(\n tag: string,\n): Text<\"inline\", TContextData> {\n return new HashtagText(tag);\n}\n\n/**\n * A text tree that renders a `<strong>` text. You normally don't need to\n * instantiate this directly; use the {@link strong} function instead.\n * @typeParam TContextData The type of the context data.\n */\nexport class StrongText<TContextData> implements Text<\"inline\", TContextData> {\n readonly type = \"inline\";\n #text: Text<\"inline\", TContextData>;\n\n /**\n * Creates a {@link StrongText} tree with a text.\n * @param text The text to render as `<strong>`.\n */\n constructor(text: Text<\"inline\", TContextData> | string) {\n this.#text = typeof text === \"string\" ? new PlainText(text) : text;\n }\n\n async *getHtml(session: Session<TContextData>): AsyncIterable<string> {\n yield \"<strong>\";\n yield* this.#text.getHtml(session);\n yield \"</strong>\";\n }\n\n getTags(session: Session<TContextData>): AsyncIterable<Link | Object> {\n return this.#text.getTags(session);\n }\n\n getCachedObjects(): Object[] {\n return [];\n }\n}\n\n/**\n * Applies `<strong>` tag to a text. You can use this function to create a\n * {@link StrongText} tree.\n * @typeParam TContextData The type of the context data.\n * @param text The text to render as `<strong>`. It can be a plain text or\n * another text tree.\n * @returns A {@link StrongText} tree.\n */\nexport function strong<TContextData>(\n text: Text<\"inline\", TContextData> | string,\n): Text<\"inline\", TContextData> {\n return new StrongText(text);\n}\n\n/**\n * A text tree that renders an `<em>` text. You normally don't need to\n * instantiate this directly; use the {@link em} function instead.\n * @typeParam TContextData The type of the context data.\n */\nexport class EmText<TContextData> implements Text<\"inline\", TContextData> {\n readonly type = \"inline\";\n #text: Text<\"inline\", TContextData>;\n\n constructor(text: Text<\"inline\", TContextData> | string) {\n this.#text = typeof text === \"string\" ? new PlainText(text) : text;\n }\n\n async *getHtml(session: Session<TContextData>): AsyncIterable<string> {\n yield \"<em>\";\n yield* this.#text.getHtml(session);\n yield \"</em>\";\n }\n\n getTags(session: Session<TContextData>): AsyncIterable<Link | Object> {\n return this.#text.getTags(session);\n }\n\n getCachedObjects(): Object[] {\n return [];\n }\n}\n\n/**\n * Applies `<em>` tag to a text. You can use this function to create an\n * {@link EmText} tree.\n * @typeParam TContextData The type of the context data.\n * @param text The text to render as `<em>`. It can be a plain text or\n * another text tree.\n * @returns A {@link EmText} tree.\n */\nexport function em<TContextData>(\n text: Text<\"inline\", TContextData> | string,\n): Text<\"inline\", TContextData> {\n return new EmText(text);\n}\n\n/**\n * A text tree that renders a link. You normally don't need to instantiate\n * this directly; use the {@link link} function instead.\n * @typeParam TContextData The type of the context data.\n */\nexport class LinkText<TContextData> implements Text<\"inline\", TContextData> {\n readonly type = \"inline\";\n #label: Text<\"inline\", TContextData>;\n #href: URL;\n\n /**\n * Creates a {@link LinkText} tree with a label and a URL.\n * @param label The label of the link.\n * @param href The URL of the link. It has to be an absolute URL.\n */\n constructor(\n label: Text<\"inline\", TContextData> | string,\n href: URL | string,\n ) {\n this.#label = typeof label === \"string\" ? new PlainText(label) : label;\n this.#href = typeof href === \"string\" ? new URL(href) : href;\n }\n\n async *getHtml(session: Session<TContextData>): AsyncIterable<string> {\n yield '<a href=\"';\n yield encode(this.#href.href);\n yield '\" target=\"_blank\">';\n yield* this.#label.getHtml(session);\n yield \"</a>\";\n }\n\n getTags(session: Session<TContextData>): AsyncIterable<Link | Object> {\n return this.#label.getTags(session);\n }\n\n getCachedObjects(): Object[] {\n return this.#label.getCachedObjects();\n }\n}\n\n/**\n * Creates a link to the given `href` with the `label`. You can use this\n * function to create a {@link LinkText} tree.\n * @typeParam TContextData The type of the context data.\n * @param label The displayed label of the link.\n * @param href The link target. It has to be an absolute URL.\n * @returns A {@link LinkText} tree.\n */\nexport function link<TContextData>(\n label: Text<\"inline\", TContextData> | string,\n href: URL | string,\n): Text<\"inline\", TContextData>;\n\n/**\n * Creates a link to the given `url` with no label. You can use this function\n * to create a {@link LinkText} tree. The label of the link will be the same\n * as the given `url`.\n * @param url The link target. It has to be an absolute URL.\n * @returns A {@link LinkText} tree.\n */\nexport function link<TContextData>(\n url: URL | string,\n): Text<\"inline\", TContextData>;\n\nexport function link<TContextData>(\n label: Text<\"inline\", TContextData> | string | URL,\n href?: URL | string,\n): Text<\"inline\", TContextData> {\n return href == null\n ? new LinkText(String(label), label as string)\n : new LinkText(\n isText<TContextData>(label) ? label : label.toString(),\n href,\n );\n}\n\n/**\n * A text tree that renders a inline code. You normally don't need to\n * instantiate this directly; use the {@link code} function instead.\n * @typeParam TContextData The type of the context data.\n */\nexport class CodeText<TContextData> implements Text<\"inline\", TContextData> {\n readonly type = \"inline\";\n readonly #code: Text<\"inline\", TContextData>;\n\n /**\n * Creates a {@link CodeText} tree with a code.\n * @param code The code to render.\n */\n constructor(code: Text<\"inline\", TContextData> | string) {\n this.#code = typeof code === \"string\" ? new PlainText(code) : code;\n }\n\n async *getHtml(session: Session<TContextData>): AsyncIterable<string> {\n yield \"<code>\";\n yield* this.#code.getHtml(session);\n yield \"</code>\";\n }\n\n getTags(session: Session<TContextData>): AsyncIterable<Link | Object> {\n return this.#code.getTags(session);\n }\n\n getCachedObjects(): Object[] {\n return [];\n }\n}\n\n/**\n * Applies `<code>` tag to a text. You can use this function to create\n * a {@link CodeText} tree.\n * @param code The code to render.\n * @returns A {@link CodeText} tree.\n */\nexport function code<TContextData>(\n code: Text<\"inline\", TContextData> | string,\n): Text<\"inline\", TContextData> {\n return new CodeText(code);\n}\n\n/**\n * A text tree that renders a custom emoji. You normally don't need to\n * instantiate this directly; use the {@link customEmoji} function instead.\n * @typeParam TContextData The type of the context data.\n * @since 0.2.0\n */\nexport class CustomEmojiText<TContextData>\n implements Text<\"inline\", TContextData> {\n readonly type = \"inline\";\n readonly #emoji: Emoji | DeferredCustomEmoji<TContextData>;\n\n /**\n * Creates a {@link CustomEmojiText} tree with a custom emoji.\n * @param emoji The custom emoji to render.\n */\n constructor(emoji: Emoji | DeferredCustomEmoji<TContextData>) {\n this.#emoji = emoji;\n }\n\n /**\n * Gets the emoji object. If the emoji is a deferred emoji, it will\n * be resolved with the given session.\n * @param session The bot session.\n * @returns The emoji object.\n */\n getEmoji(session: Session<TContextData>): Emoji {\n if (typeof this.#emoji === \"function\") return this.#emoji(session);\n return this.#emoji;\n }\n\n async *getHtml(session: Session<TContextData>): AsyncIterable<string> {\n const emoji = this.getEmoji(session);\n if (emoji.name == null) return;\n yield \"\\u200b\"; // zero-width space for segmentation\n yield encode(emoji.name.toString());\n yield \"\\u200b\";\n }\n\n async *getTags(\n session: Session<TContextData>,\n ): AsyncIterable<Link | Object> {\n yield this.getEmoji(session);\n }\n\n getCachedObjects(): Object[] {\n return [];\n }\n}\n\n/**\n * Renders a custom emoji. You can use this function to create a\n * {@link CustomEmojiText} tree.\n * @param emoji The custom emoji to render. See also {@link Bot.addCustomEmojis}\n * method.\n * @returns A {@link CustomEmojiText} tree.\n * @since 0.2.0\n */\nexport function customEmoji<TContextData>(\n emoji: Emoji | DeferredCustomEmoji<TContextData>,\n): Text<\"inline\", TContextData> {\n return new CustomEmojiText(emoji);\n}\n\n/**\n * The options for rendering a Markdown text.\n */\nexport interface MarkdownTextOptions {\n /**\n * Whether to render mentions in the Markdown text.\n * @default {true}\n */\n readonly mentions?: boolean;\n\n /**\n * Whether to render hashtags in the Markdown text.\n * @default {true}\n */\n readonly hashtags?: boolean;\n\n /**\n * Whether to automatically linkify URLs in the Markdown text.\n * @default {true}\n */\n readonly linkify?: boolean;\n}\n\ninterface MarkdownEnv {\n mentions: string[];\n hashtags: string[];\n origin: string;\n actors?: Record<string, string | null>;\n}\n\n/**\n * A text tree that renders a Markdown text. You normally don't need to\n * instantiate this directly; use the {@link markdown} function instead.\n */\nexport class MarkdownText<TContextData> implements Text<\"block\", TContextData> {\n readonly type = \"block\";\n readonly #content: string;\n readonly #markdownIt: MarkdownIt;\n readonly #mentions?: string[];\n readonly #hashtags?: string[];\n #actors?: Record<string, Object>;\n\n /**\n * Creates a {@link MarkdownText} tree with a Markdown content.\n * @param content The Markdown content.\n * @param options The options for rendering the Markdown content.\n */\n constructor(content: string, options: MarkdownTextOptions = {}) {\n this.#content = content;\n const md = MarkdownIt({\n html: false,\n linkify: options.linkify ?? true,\n });\n if (options.mentions ?? true) {\n md.use(mentionPlugin, {\n link(handle: string, env: MarkdownEnv) {\n if (env.actors == null) return `acct:${handle}`;\n return env.actors[handle] ?? null;\n },\n linkAttributes(_handle: string, _env: MarkdownEnv) {\n return {\n translate: \"no\",\n class: \"h-card u-url mention\",\n target: \"_blank\",\n };\n },\n label: toFullHandle,\n });\n const env: MarkdownEnv = {\n mentions: [],\n hashtags: [],\n origin: \"http://localhost\",\n };\n md.render(content, env);\n this.#mentions = env.mentions;\n }\n if (options.hashtags ?? true) {\n md.use(hashtagPlugin, {\n link(hashtag: string, env: MarkdownEnv) {\n const tag = hashtag.substring(1).toLowerCase();\n return new URL(`/tags/${encodeURIComponent(tag)}`, env.origin).href;\n },\n linkAttributes(_hashtag: string, _env: MarkdownEnv) {\n return {\n class: \"mention hashtag\",\n rel: \"tag\",\n target: \"_blank\",\n };\n },\n label(hashtag: string, _env: MarkdownEnv) {\n const tag = hashtag.substring(1);\n return `#<span>${tag}</span>`;\n },\n });\n const env: MarkdownEnv = {\n mentions: [],\n hashtags: [],\n origin: \"http://localhost\",\n };\n md.render(content, env);\n this.#hashtags = env.hashtags;\n }\n this.#markdownIt = md;\n }\n\n async #getMentionedActors(\n session: Session<TContextData>,\n ): Promise<Record<string, Object>> {\n if (this.#mentions == null) return {};\n if (this.#actors != null) return this.#actors;\n const documentLoader = await session.context.getDocumentLoader(session.bot);\n const objects = await Promise.all(\n this.#mentions.map((m) =>\n m === session.actorHandle\n ? session.getActor()\n : session.context.lookupObject(m, { documentLoader })\n ),\n );\n const actors: Record<string, Object> = {};\n for (let i = 0; i < this.#mentions.length; i++) {\n const object = objects[i];\n if (object != null) actors[this.#mentions[i]] = object;\n }\n this.#actors = actors;\n return actors;\n }\n\n async *getHtml(session: Session<TContextData>): AsyncIterable<string> {\n if (this.#mentions == null) {\n yield this.#markdownIt.render(this.#content);\n return;\n }\n const actors: Record<string, string | null> = globalThis.Object.fromEntries(\n globalThis.Object.entries(\n await this.#getMentionedActors(session),\n ).filter(([_, obj]) => isActor(obj)).map((\n [handle, actor],\n ) =>\n [\n handle,\n (actor.url instanceof Link\n ? actor.url.href?.href\n : actor.url?.href) ?? actor.id?.href ?? null,\n ] satisfies [string, string | null]\n ).filter(([_, url]) => url != null),\n );\n const env: MarkdownEnv = {\n mentions: [],\n hashtags: [],\n origin: session.context.origin,\n actors,\n };\n yield this.#markdownIt.render(this.#content, env);\n }\n\n async *getTags(session: Session<TContextData>): AsyncIterable<Link | Object> {\n if (this.#mentions == null) return;\n const actors = await this.#getMentionedActors(session);\n for (const [handle, object] of globalThis.Object.entries(actors)) {\n if (!isActor(object) || object.id == null) continue;\n yield new Mention({\n name: handle,\n href: object.id,\n });\n }\n if (this.#hashtags != null) {\n for (const hashtag of this.#hashtags) {\n const tag = hashtag.substring(1).toLowerCase();\n yield new Hashtag({\n name: `#${tag}`,\n href: new URL(\n `/tags/${encodeURIComponent(tag)}`,\n session.context.origin,\n ),\n });\n }\n }\n }\n\n getCachedObjects(): Object[] {\n return this.#actors == null ? [] : globalThis.Object.values(this.#actors);\n }\n}\n\n/**\n * Renders a Markdown text. You can use this function to create\n * a {@link MarkdownText} tree. The mentions in the Markdown text\n * will be rendered as links unless the `mentions` option is set to\n * `false`.\n * @param content The Markdown content.\n * @param options The options for rendering the Markdown content.\n * @returns A {@link MarkdownText} tree.\n */\nexport function markdown<TContextData>(\n content: string,\n options: MarkdownTextOptions = {},\n): Text<\"block\", TContextData> {\n return new MarkdownText(content, options);\n}\n"],"mappings":"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"}
|