├── .cursorrules ├── .devcontainer ├── bin │ ├── postAttachCommand.sh │ └── postCreateCommand.sh └── devcontainer.json ├── .github ├── FUNDING.yaml ├── copilot-instructions.md └── workflows │ ├── ai-labeler.yaml │ ├── build.yaml │ └── sponsors.yaml ├── .gitignore ├── .repomixignore ├── .vscode ├── extensions.json └── settings.json ├── .windsurfrules ├── .zed └── settings.json ├── CHANGES.md ├── CLAUDE.md ├── CONTRIBUTING.md ├── FEDERATION.md ├── LICENSE ├── README.md ├── SECURITY.md ├── SPONSORS.md ├── cli ├── .gitignore ├── README.md ├── cache.ts ├── deno.json ├── docloader.ts ├── inbox.tsx ├── inbox │ ├── entry.ts │ ├── rendercode.ts │ ├── test.tsx │ └── view.tsx ├── init.ts ├── log.ts ├── lookup.ts ├── mod.ts ├── node.ts ├── npm │ ├── .gitignore │ ├── install.mjs │ ├── package.json │ └── run.mjs ├── scripts │ ├── check_version.ts │ ├── npm.ts │ ├── pack.ts │ └── sync_version.ts ├── table.ts ├── tempserver.ts ├── tunnel.ts └── utils.ts ├── cspell.json ├── deno.json ├── docs ├── .gitignore ├── .vitepress │ ├── .gitignore │ ├── config.mts │ └── theme │ │ ├── index.ts │ │ └── style.css ├── README.md ├── changelog.md ├── cli.md ├── cli │ ├── fedify-inbox-web.png │ └── fedify-node.png ├── contribute.md ├── index.md ├── install.md ├── intro.md ├── manual │ ├── access-control.md │ ├── actor.md │ ├── collections.md │ ├── context.md │ ├── deploy.md │ ├── federation.md │ ├── inbox.md │ ├── integration.md │ ├── kv.md │ ├── log.md │ ├── mq.md │ ├── nodeinfo.md │ ├── object.md │ ├── opentelemetry.md │ ├── pragmatics.md │ ├── pragmatics │ │ ├── mastodon-automated.png │ │ ├── mastodon-avatar.png │ │ ├── mastodon-custom-fields.png │ │ ├── mastodon-followers.png │ │ ├── mastodon-following.png │ │ ├── mastodon-group.png │ │ ├── mastodon-header.png │ │ ├── mastodon-lock.png │ │ ├── mastodon-memorial.png │ │ ├── mastodon-person.png │ │ └── mastodon-suspended.png │ ├── send.md │ ├── test.md │ └── vocab.md ├── package.json ├── pnpm-lock.yaml ├── public │ ├── favicon-192x192.png │ ├── favicon-32x32.png │ ├── logo.png │ └── logo.svg ├── security.md ├── sponsors.md ├── tutorial.md ├── tutorial │ ├── basics.md │ ├── basics │ │ └── search-result.png │ ├── microblog.md │ └── microblog │ │ ├── 404.png │ │ ├── academy-compose.png │ │ ├── academy-notifications.png │ │ ├── academy-profile.png │ │ ├── academy-search-results-2.png │ │ ├── academy-search-results.png │ │ ├── academy-timeline.png │ │ ├── academy.jpg │ │ ├── account-creation-page-2.png │ │ ├── account-creation-page.png │ │ ├── activity-log-2.png │ │ ├── activity-log-3.png │ │ ├── activity-log-4.png │ │ ├── activity-log-5.png │ │ ├── activity-log-6.png │ │ ├── activity-log-7.png │ │ ├── activity-log-8.png │ │ ├── activity-log.png │ │ ├── followers-list.png │ │ ├── following-list.png │ │ ├── home-2.png │ │ ├── home-3.png │ │ ├── home-4.png │ │ ├── home-5.png │ │ ├── home-6.png │ │ ├── home.png │ │ ├── post-page.png │ │ ├── profile-page-2.png │ │ ├── profile-page-3.png │ │ ├── profile-page-4.png │ │ ├── profile-page-5.png │ │ ├── profile-page.png │ │ ├── remote-profile-2.png │ │ ├── remote-profile.png │ │ ├── search-results-2.png │ │ ├── search-results-3.png │ │ └── search-results.png └── why.md ├── examples ├── README.md ├── actor-lookup-cli │ ├── README.md │ └── main.ts ├── blog │ ├── .gitignore │ ├── README.md │ ├── components │ │ ├── Comment.tsx │ │ ├── Post.tsx │ │ ├── PostForm.tsx │ │ ├── PostList.tsx │ │ └── Setup.tsx │ ├── deno.json │ ├── dev.ts │ ├── federation │ │ └── mod.ts │ ├── fresh.config.ts │ ├── fresh.gen.ts │ ├── images │ │ ├── handle.png │ │ └── setup.png │ ├── loggers.ts │ ├── main.ts │ ├── models │ │ ├── blog.ts │ │ ├── comment.ts │ │ ├── follower.ts │ │ ├── kv.ts │ │ └── post.ts │ ├── routes │ │ ├── _app.tsx │ │ ├── _middleware.ts │ │ ├── followers.tsx │ │ ├── index.tsx │ │ ├── posts │ │ │ ├── [uuid].tsx │ │ │ └── index.tsx │ │ └── users │ │ │ └── [handle].ts │ └── static │ │ └── styles.css ├── cloudflare-workers │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── pnpm-lock.yaml │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ ├── worker-configuration.d.ts │ └── wrangler.jsonc ├── express │ ├── .gitignore │ ├── .nvmrc │ ├── README.md │ ├── app.ts │ ├── package-lock.json │ ├── package.json │ └── tsconfig.json ├── hono-sample │ ├── README.md │ ├── deno.json │ └── main.ts ├── next14-app-router │ ├── .eslintrc.json │ ├── .gitignore │ ├── .nvmrc │ ├── README.md │ ├── app │ │ ├── [fedify] │ │ │ └── [[...catchAll]] │ │ │ │ └── route.ts │ │ ├── globals.css │ │ ├── layout.tsx │ │ └── page.tsx │ ├── data │ │ └── store.ts │ ├── instrumentation.ts │ ├── next.config.ts │ ├── package-lock.json │ ├── package.json │ ├── postcss.config.mjs │ ├── shared │ │ └── integrate-fedify.ts │ ├── tailwind.config.ts │ └── tsconfig.json └── next15-app-router │ ├── .gitignore │ ├── .nvmrc │ ├── README.md │ ├── app │ ├── .well-known │ │ └── [[...catchAll]] │ │ │ └── route.ts │ ├── [slug] │ │ └── page.tsx │ ├── favicon.ico │ ├── fedify-activity-handler │ │ └── [[...catchAll]] │ │ │ └── route.ts │ ├── globals.css │ ├── layout.tsx │ └── page.tsx │ ├── bun.lock │ ├── data │ └── store.ts │ ├── eslint.config.mjs │ ├── next.config.ts │ ├── package.json │ ├── postcss.config.mjs │ ├── shared │ └── integrate-fedify.ts │ └── tsconfig.json ├── fedify ├── .gitignore ├── README.md ├── cfworkers │ ├── client.ts │ └── server.ts ├── codegen │ ├── __snapshots__ │ │ └── class.test.ts.snap │ ├── class.test.ts │ ├── class.ts │ ├── codec.ts │ ├── constructor.ts │ ├── field.ts │ ├── fs.test.ts │ ├── fs.ts │ ├── inspector.ts │ ├── main.ts │ ├── property.ts │ ├── schema.ts │ ├── schema.yaml │ └── type.ts ├── compat │ ├── mod.ts │ ├── transformers.test.ts │ ├── transformers.ts │ └── types.ts ├── deno.json ├── federation │ ├── builder.test.ts │ ├── builder.ts │ ├── callback.ts │ ├── collection.test.ts │ ├── collection.ts │ ├── context.ts │ ├── federation.ts │ ├── handler.test.ts │ ├── handler.ts │ ├── inbox.test.ts │ ├── inbox.ts │ ├── keycache.test.ts │ ├── keycache.ts │ ├── kv.test.ts │ ├── kv.ts │ ├── middleware.test.ts │ ├── middleware.ts │ ├── mod.ts │ ├── mq.test.ts │ ├── mq.ts │ ├── negotiation.ts │ ├── queue.ts │ ├── retry.test.ts │ ├── retry.ts │ ├── router.test.ts │ ├── router.ts │ ├── send.test.ts │ └── send.ts ├── mod.ts ├── nodeinfo │ ├── client.test.ts │ ├── client.ts │ ├── handler.test.ts │ ├── handler.ts │ ├── mod.ts │ ├── semver.test.ts │ ├── semver.ts │ ├── types.test.ts │ └── types.ts ├── package.json ├── runtime │ ├── authdocloader.test.ts │ ├── authdocloader.ts │ ├── contexts.ts │ ├── docloader.test.ts │ ├── docloader.ts │ ├── key.test.ts │ ├── key.ts │ ├── langstr.test.ts │ ├── langstr.ts │ ├── mod.ts │ ├── multibase │ │ ├── base.ts │ │ ├── constants.ts │ │ ├── index.ts │ │ ├── multibase.test.ts │ │ ├── rfc4648.ts │ │ ├── types.d.ts │ │ └── util.ts │ ├── url.test.ts │ └── url.ts ├── scripts │ ├── check_version.ts │ ├── discover_tests.ts │ └── sync_version.ts ├── shim │ └── event.ts ├── sig │ ├── http.test.ts │ ├── http.ts │ ├── key.test.ts │ ├── key.ts │ ├── ld.test.ts │ ├── ld.ts │ ├── mod.ts │ ├── owner.test.ts │ ├── owner.ts │ ├── proof.test.ts │ └── proof.ts ├── testing │ ├── context.ts │ ├── docloader.test.ts │ ├── docloader.ts │ ├── fixtures │ │ ├── activitypub.academy │ │ │ └── users │ │ │ │ └── brauca_darradiul.json │ │ ├── example.com │ │ │ ├── announce.json │ │ │ ├── collection.json │ │ │ ├── create.json │ │ │ ├── cross-origin-actor.json │ │ │ ├── hong-gildong.json │ │ │ ├── invite.json │ │ │ ├── key.json │ │ │ ├── key2.json │ │ │ ├── object.json │ │ │ ├── orderedcollectionpage.json │ │ │ ├── paged-collection.json │ │ │ ├── paged │ │ │ │ ├── a.json │ │ │ │ └── b.json │ │ │ ├── person.json │ │ │ ├── person2.json │ │ │ ├── test.json │ │ │ ├── users │ │ │ │ └── handle.json │ │ │ └── wrong-type.json │ │ ├── remote.domain │ │ │ └── users │ │ │ │ └── bob.json │ │ ├── server.example │ │ │ └── users │ │ │ │ └── alice.json │ │ ├── w3id.org │ │ │ ├── identity │ │ │ │ └── v1.json │ │ │ └── security │ │ │ │ ├── data-integrity │ │ │ │ └── v1.json │ │ │ │ ├── multikey │ │ │ │ └── v1.json │ │ │ │ └── v1.json │ │ ├── wizard.casa │ │ │ └── users │ │ │ │ └── hongminhee.json │ │ └── www.w3.org │ │ │ └── ns │ │ │ ├── activitystreams.json │ │ │ └── did │ │ │ └── v1.json │ ├── keys.ts │ └── mod.ts ├── tsdown.config.ts ├── vocab │ ├── .gitignore │ ├── __snapshots__ │ │ └── vocab.test.ts.snap │ ├── accept.yaml │ ├── activity.yaml │ ├── actor.test.ts │ ├── actor.ts │ ├── add.yaml │ ├── announce.yaml │ ├── application.yaml │ ├── arrive.yaml │ ├── article.yaml │ ├── audio.yaml │ ├── block.yaml │ ├── chatmessage.yaml │ ├── collection.yaml │ ├── collectionpage.yaml │ ├── constants.ts │ ├── create.yaml │ ├── dataintegrityproof.yaml │ ├── delete.yaml │ ├── didservice.yaml │ ├── dislike.yaml │ ├── document.yaml │ ├── emoji.yaml │ ├── emojireact.yaml │ ├── endpoints.yaml │ ├── event.yaml │ ├── export.yaml │ ├── flag.yaml │ ├── follow.yaml │ ├── group.yaml │ ├── hashtag.yaml │ ├── ignore.yaml │ ├── image.yaml │ ├── intransitiveactivity.yaml │ ├── invite.yaml │ ├── join.yaml │ ├── key.yaml │ ├── leave.yaml │ ├── like.yaml │ ├── link.yaml │ ├── listen.yaml │ ├── lookup.test.ts │ ├── lookup.ts │ ├── mention.yaml │ ├── mod.ts │ ├── move.yaml │ ├── multikey.yaml │ ├── note.yaml │ ├── object.yaml │ ├── offer.yaml │ ├── orderedcollection.yaml │ ├── orderedcollectionpage.yaml │ ├── organization.yaml │ ├── page.yaml │ ├── person.yaml │ ├── place.yaml │ ├── profile.yaml │ ├── propertyvalue.yaml │ ├── question.yaml │ ├── read.yaml │ ├── reject.yaml │ ├── relationship.yaml │ ├── remove.yaml │ ├── service.yaml │ ├── source.yaml │ ├── tentativeaccept.yaml │ ├── tentativereject.yaml │ ├── tombstone.yaml │ ├── travel.yaml │ ├── type.test.ts │ ├── type.ts │ ├── undo.yaml │ ├── update.yaml │ ├── video.yaml │ ├── view.yaml │ ├── vocab.bench.ts │ └── vocab.test.ts ├── webfinger │ ├── handler.test.ts │ ├── handler.ts │ ├── jrd.ts │ ├── lookup.test.ts │ ├── lookup.ts │ └── mod.ts ├── wrangler.toml └── x │ ├── cfworkers.test.ts │ ├── cfworkers.ts │ ├── denokv.test.ts │ ├── denokv.ts │ ├── fresh.ts │ ├── hono.ts │ └── sveltekit.ts ├── logo.svg ├── mise.toml ├── repomix.config.json └── scripts └── sponsors.ts /.cursorrules: -------------------------------------------------------------------------------- 1 | .github/copilot-instructions.md -------------------------------------------------------------------------------- /.devcontainer/bin/postAttachCommand.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # Run codegen 6 | deno task -c src/deno.json codegen 7 | deno task -c cli/deno.json codegen 8 | 9 | # .bashrc doesn't work when creating a new container. 10 | export NVM_DIR="$HOME/.nvm" 11 | [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm 12 | [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion 13 | export PATH="$PATH:$HOME/.bun/bin" 14 | 15 | echo 16 | echo "INFO: Tool version" 17 | echo "node: $(node -v)" 18 | deno -v 19 | echo "bun: $(bun -v)" 20 | echo "pnpm: $(pnpm -v)" 21 | echo 22 | -------------------------------------------------------------------------------- /.devcontainer/bin/postCreateCommand.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | export BUN_VERSION="v1.1.29" 6 | export NODE_VERSION="22" 7 | export NVM_VERSION="v0.40.0" 8 | export PNPM_VERSION="9.11.0" 9 | 10 | # Setup deno completions 11 | mkdir -p /usr/local/etc/bash_completion.d/ 12 | deno completions bash > /usr/local/etc/bash_completion.d/deno.bash 13 | 14 | cat << EOF >> ~/.bashrc 15 | source /usr/local/etc/bash_completion.d/deno.bash 16 | EOF 17 | 18 | apt update 19 | apt upgrade -y 20 | 21 | apt install -y git curl unzip vim 22 | 23 | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/$NVM_VERSION/install.sh | bash 24 | 25 | export NVM_DIR="$HOME/.nvm" 26 | [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm 27 | [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion 28 | 29 | nvm install "$NODE_VERSION" 30 | 31 | corepack enable 32 | corepack install -g "pnpm@$PNPM_VERSION" 33 | 34 | curl -fsSL https://bun.sh/install | bash -s "bun-$BUN_VERSION" 35 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node 3 | { 4 | "name": "Development Environment", 5 | "image": "denoland/deno:ubuntu-1.46.3", 6 | "postCreateCommand": ".devcontainer/bin/postCreateCommand.sh", 7 | "postAttachCommand": ".devcontainer/bin/postAttachCommand.sh", 8 | "customizations": { 9 | "vscode": { 10 | "extensions": [ 11 | "denoland.vscode-deno", 12 | "redhat.vscode-yaml", 13 | "streetsidesoftware.code-spell-checker" 14 | ] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.github/FUNDING.yaml: -------------------------------------------------------------------------------- 1 | open_collective: fedify 2 | github: dahlia 3 | -------------------------------------------------------------------------------- /.github/workflows/ai-labeler.yaml: -------------------------------------------------------------------------------- 1 | name: ai-labeler 2 | on: 3 | issues: 4 | types: [opened, edited, reopened, labeled] 5 | pull_request_target: 6 | types: [opened, edited, reopened, labeled] 7 | 8 | jobs: 9 | ai-labeler: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: read 13 | issues: write 14 | pull-requests: write 15 | steps: 16 | - uses: actions/checkout@v4 17 | - if: >- 18 | github.event.action != 'labeled' || 19 | github.event.label.name == 'status/needs triage' 20 | uses: jlowin/ai-labeler@v0.5.1 21 | with: 22 | include-repo-labels: true 23 | openai-api-key: ${{ secrets.OPENAI_API_KEY }} 24 | - if: >- 25 | github.event.action == 'labeled' && 26 | github.event.label.name == 'status/needs triage' 27 | uses: actions-ecosystem/action-remove-labels@v1 28 | with: 29 | labels: "status/needs triage" 30 | -------------------------------------------------------------------------------- /.github/workflows/sponsors.yaml: -------------------------------------------------------------------------------- 1 | name: sponsors 2 | on: 3 | schedule: 4 | # ┌───────────── minute (0 - 59) 5 | # │ ┌───────────── hour (0 - 23) 6 | # │ │ ┌───────────── day of the month (1 - 31) 7 | # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) 8 | # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT) 9 | # │ │ │ │ │ 10 | # │ │ │ │ │ 11 | # │ │ │ │ │ 12 | - cron: '0 * * * *' 13 | workflow_dispatch: 14 | 15 | jobs: 16 | update-sponsors: 17 | runs-on: ubuntu-latest 18 | permissions: 19 | contents: write 20 | pull-requests: write 21 | steps: 22 | - uses: actions/checkout@v4 23 | with: 24 | ssh-key: ${{ secrets.SSH_PRIVATE_KEY }} 25 | persist-credentials: true 26 | fetch-depth: 0 27 | - uses: denoland/setup-deno@v1 28 | with: 29 | deno-version: v2.x 30 | - uses: qoomon/actions--setup-git@v1 31 | - run: | 32 | set -e 33 | deno run \ 34 | --allow-net=opencollective.com \ 35 | --allow-read=README.md,SPONSORS.md \ 36 | --allow-write=README.md,SPONSORS.md \ 37 | scripts/sponsors.ts \ 38 | README.md \ 39 | SPONSORS.md 40 | cat SPONSORS.md 41 | if ! git diff-index --quiet HEAD --; then 42 | git add README.md SPONSORS.md 43 | git commit -m "Update sponsors" || true 44 | fi 45 | - uses: ad-m/github-push-action@master 46 | with: 47 | ssh: true 48 | branch: main 49 | 50 | # cSpell: ignore qoomon opencollective 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .claude/ 2 | .DS_Store 3 | deno.lock 4 | repomix-output.xml 5 | t.ts 6 | t2.ts 7 | 8 | **/.claude/settings.local.json 9 | -------------------------------------------------------------------------------- /.repomixignore: -------------------------------------------------------------------------------- 1 | .devcontainer/ 2 | .git/ 3 | .github/ 4 | .vscode/ 5 | .zed/ 6 | repomix-output.xml 7 | src/codegen/__snapshots__/ 8 | src/vocab/__snapshots__/ 9 | src/npm/ 10 | scripts/ 11 | t.ts 12 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "denoland.vscode-deno", 4 | "redhat.vscode-yaml", 5 | "streetsidesoftware.code-spell-checker" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true, 3 | "deno.unstable": true, 4 | "editor.detectIndentation": false, 5 | "editor.indentSize": 2, 6 | "editor.insertSpaces": true, 7 | "files.eol": "\n", 8 | "files.insertFinalNewline": true, 9 | "files.trimFinalNewlines": true, 10 | "yaml.completion": true, 11 | "yaml.format.enable": false, 12 | "yaml.hover": true, 13 | "yaml.schemas": { 14 | "src/codegen/schema.yaml": "/src/vocab/*.yaml", 15 | "https://json-schema.org/draft/2020-12/schema": "/src/codegen/schema.yaml" 16 | }, 17 | "yaml.validate": true, 18 | "[javascript]": { 19 | "editor.defaultFormatter": "denoland.vscode-deno", 20 | "editor.formatOnSave": true, 21 | "editor.codeActionsOnSave": { 22 | "source.sortImports": "always" 23 | } 24 | }, 25 | "[javascriptreact]": { 26 | "editor.defaultFormatter": "denoland.vscode-deno", 27 | "editor.formatOnSave": true, 28 | "editor.codeActionsOnSave": { 29 | "source.sortImports": "always" 30 | } 31 | }, 32 | "[json]": { 33 | "editor.defaultFormatter": "vscode.json-language-features", 34 | "editor.formatOnSave": true 35 | }, 36 | "[jsonc]": { 37 | "editor.defaultFormatter": "vscode.json-language-features", 38 | "editor.formatOnSave": true 39 | }, 40 | "[typescript]": { 41 | "editor.defaultFormatter": "denoland.vscode-deno", 42 | "editor.formatOnSave": true, 43 | "editor.codeActionsOnSave": { 44 | "source.sortImports": "always" 45 | } 46 | }, 47 | "[typescriptreact]": { 48 | "editor.defaultFormatter": "denoland.vscode-deno", 49 | "editor.formatOnSave": true, 50 | "editor.codeActionsOnSave": { 51 | "source.sortImports": "always" 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /.windsurfrules: -------------------------------------------------------------------------------- 1 | .github/copilot-instructions.md -------------------------------------------------------------------------------- /.zed/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno": { 3 | "enable": true, 4 | "unstable": true 5 | }, 6 | "ensure_final_newline_on_save": true, 7 | "format_on_save": "on", 8 | "formatter": "language_server", 9 | "languages": { 10 | "JSON": { 11 | "language_servers": [ 12 | "deno", 13 | "!biome" 14 | ] 15 | }, 16 | "JavaScript": { 17 | "code_actions_on_format": { 18 | "source.sortImports": true 19 | }, 20 | "language_servers": [ 21 | "deno", 22 | "!typescript-language-server", 23 | "!vtsls", 24 | "!biome", 25 | "..." 26 | ] 27 | }, 28 | "TypeScript": { 29 | "code_actions_on_format": { 30 | "source.sortImports": true 31 | }, 32 | "language_servers": [ 33 | "deno", 34 | "!typescript-language-server", 35 | "!vtsls", 36 | "!biome", 37 | "..." 38 | ] 39 | }, 40 | "TSX": { 41 | "code_actions_on_format": { 42 | "source.sortImports": true 43 | }, 44 | "language_servers": [ 45 | "deno", 46 | "!typescript-language-server", 47 | "!vtsls", 48 | "!biome", 49 | "..." 50 | ] 51 | }, 52 | "YAML": { 53 | "language_servers": [ 54 | "!deno", 55 | "!biome" 56 | ], 57 | "enable_language_server": false 58 | } 59 | }, 60 | "show_wrap_guides": true, 61 | "wrap_guides": [ 62 | 80 63 | ] 64 | } 65 | -------------------------------------------------------------------------------- /CLAUDE.md: -------------------------------------------------------------------------------- 1 | .github/copilot-instructions.md -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright 2024–2025 Hong Minhee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | fedify/README.md -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Security policy 4 | =============== 5 | 6 | Supported versions 7 | ------------------ 8 | 9 | We support the latest two minor versions of the library. For example, if the 10 | latest version is 0.12.0, we support 0.12.x and 0.11.x. 11 | 12 | 13 | Reporting a vulnerability 14 | ------------------------- 15 | 16 | If you think you have found a security issue, please *do not* open a public 17 | issue. Instead, please open a private vulnerability report by [creating a new 18 | draft security advisory][1]. 19 | 20 | We will review your report and respond within 48 hours. 21 | 22 | [1]: https://github.com/fedify-dev/fedify/security/advisories/new 23 | -------------------------------------------------------------------------------- /SPONSORS.md: -------------------------------------------------------------------------------- 1 | Sponsors of Fedify 2 | ================== 3 | 4 | This project exists thanks to all the people who contribute, donate, and sponsor 5 | it. We are grateful for their support. We would like to thank the following 6 | financial contributors:[^1] 7 | 8 | [^1]: Those lists are automatically updated every hour. 9 | 10 | 11 | 12 | 13 | Corporate sponsors 14 | ------------------ 15 | 16 | - [ Ghost](https://ghost.org) 17 | 18 | Supporters 19 | ---------- 20 | 21 | - [Daniel Supernault](https://pixelfed.org/) 22 | - [tkgka](https://opencollective.com/tkgka) 23 | - [Blaine](https://opencollective.com/blaine) 24 | - [Erick González Aguilar](https://opencollective.com/erick-gonzalez-aguilar) 25 | 26 | Backers 27 | ------- 28 | 29 | Robin Riley, yamanoku, taye, Encyclia, okin, Andy Piper, box464, Evan Prodromou, Rafael Goulart, malte 30 | 31 | One-time donations 32 | ------------------ 33 | 34 | Robin Riley, Markus P, Nils Bergmann, Rameez 35 | 36 | 37 | 38 | 39 | 40 | Become a sponsor 41 | ---------------- 42 | 43 | We welcome financial contributions to help us maintain and improve this project. 44 | If you would like to become a financial contributor, please visit our 45 | [Open Collective]. 46 | 47 | [Open Collective]: https://opencollective.com/fedify 48 | -------------------------------------------------------------------------------- /cli/.gitignore: -------------------------------------------------------------------------------- 1 | fedify-cli-*.tar.xz 2 | fedify-cli-*.tgz 3 | fedify-cli-*.zip 4 | -------------------------------------------------------------------------------- /cli/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | `fedify`: the CLI toolchain for Fedify and debugging ActivityPub 4 | ================================================================ 5 | 6 | [![JSR][JSR badge]][JSR] 7 | [![npm][npm badge]][npm] 8 | [![GitHub Releases][GitHub Releases badge]][GitHub Releases] 9 | 10 | The `fedify` is a CLI toolchain for Fedify and debugging ActivityPub-enabled 11 | federated server apps. Although it is primarily designed for developers who use 12 | [Fedify], it can be used with any ActivityPub-enabled server. 13 | 14 | [JSR]: https://jsr.io/@fedify/cli 15 | [JSR badge]: https://jsr.io/badges/@fedify/cli 16 | [npm]: https://www.npmjs.com/package/@fedify/cli 17 | [npm badge]: https://img.shields.io/npm/v/@fedify/cli?logo=npm 18 | [GitHub Releases]: https://github.com/fedify-dev/fedify/releases 19 | [GitHub Releases badge]: https://img.shields.io/github/v/release/fedify-dev/fedify?sort=semver&logo=github 20 | [Fedify]: https://fedify.dev/ 21 | 22 | 23 | Installation 24 | ------------ 25 | 26 | ### Using npm 27 | 28 | If you have npm installed, you can install `fedify` by running the following 29 | command: 30 | 31 | ~~~~ sh 32 | npm install -g @fedify/cli 33 | ~~~~ 34 | 35 | ### Using Bun 36 | 37 | If you have [Bun] installed, you can install `fedify` by running the following 38 | command: 39 | 40 | ~~~~ sh [Bun] 41 | bun install -g @fedify/cli 42 | ~~~~ 43 | 44 | [Bun]: https://bun.sh/ 45 | 46 | ### Using Deno 47 | 48 | If you have [Deno] installed, you can install `fedify` by running the following 49 | command: 50 | 51 | ~~~~ sh 52 | # Linux/macOS 53 | deno install \ 54 | -A \ 55 | --unstable-fs --unstable-kv --unstable-temporal \ 56 | -n fedify \ 57 | jsr:@fedify/cli 58 | ~~~~ 59 | 60 | ~~~~ powershell 61 | # Windows 62 | deno install ` 63 | -A ` 64 | --unstable-fs --unstable-kv --unstable-temporal ` 65 | -n fedify ` 66 | jsr:@fedify/cli 67 | ~~~~ 68 | 69 | [Deno]: https://deno.com/ 70 | 71 | ### Downloading the executable 72 | 73 | You can download the pre-built executables from the [releases] page. Download 74 | the appropriate executable for your platform and put it in your `PATH`. 75 | 76 | [releases]: https://github.com/fedify-dev/fedify/releases 77 | -------------------------------------------------------------------------------- /cli/cache.ts: -------------------------------------------------------------------------------- 1 | import { dir } from "@cross/dir"; 2 | import { ensureDir } from "@std/fs"; 3 | import { join } from "@std/path"; 4 | 5 | export const DEFAULT_CACHE_DIR = join(await dir("cache", true), "fedify"); 6 | 7 | let currentCacheDir: string = DEFAULT_CACHE_DIR; 8 | 9 | export async function getCacheDir(): Promise { 10 | await ensureDir(currentCacheDir); 11 | return currentCacheDir; 12 | } 13 | 14 | export function setCacheDir(dir: string): Promise { 15 | currentCacheDir = dir; 16 | return Promise.resolve(); 17 | } 18 | -------------------------------------------------------------------------------- /cli/docloader.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type DocumentLoader, 3 | getDocumentLoader as getDefaultDocumentLoader, 4 | kvCache, 5 | } from "@fedify/fedify"; 6 | import { DenoKvStore } from "@fedify/fedify/x/denokv"; 7 | import { join } from "@std/path"; 8 | import { getCacheDir } from "./cache.ts"; 9 | 10 | const documentLoaders: Record = {}; 11 | 12 | export interface DocumentLoaderOptions { 13 | userAgent?: string; 14 | } 15 | 16 | export async function getDocumentLoader( 17 | { userAgent }: DocumentLoaderOptions = {}, 18 | ): Promise { 19 | if (documentLoaders[userAgent ?? ""]) return documentLoaders[userAgent ?? ""]; 20 | const path = join(await getCacheDir(), "kv"); 21 | const kv = new DenoKvStore(await Deno.openKv(path)); 22 | return documentLoaders[userAgent ?? ""] = kvCache({ 23 | kv, 24 | rules: [ 25 | [ 26 | new URLPattern({ 27 | protocol: "http{s}?", 28 | hostname: "localhost", 29 | port: "*", 30 | pathname: "/*", 31 | search: "*", 32 | hash: "*", 33 | }), 34 | Temporal.Duration.from({ seconds: 0 }), 35 | ], 36 | [ 37 | new URLPattern({ 38 | protocol: "http{s}?", 39 | hostname: "127.0.0.1", 40 | port: "*", 41 | pathname: "/*", 42 | search: "*", 43 | hash: "*", 44 | }), 45 | Temporal.Duration.from({ seconds: 0 }), 46 | ], 47 | [ 48 | new URLPattern({ 49 | protocol: "http{s}?", 50 | hostname: "\\[\\:\\:1\\]", 51 | port: "*", 52 | pathname: "/*", 53 | search: "*", 54 | hash: "*", 55 | }), 56 | Temporal.Duration.from({ seconds: 0 }), 57 | ], 58 | ], 59 | loader: getDefaultDocumentLoader({ 60 | allowPrivateAddress: true, 61 | userAgent, 62 | }), 63 | }); 64 | } 65 | 66 | export function getContextLoader( 67 | options: DocumentLoaderOptions = {}, 68 | ): Promise { 69 | return getDocumentLoader(options); 70 | } 71 | -------------------------------------------------------------------------------- /cli/inbox/entry.ts: -------------------------------------------------------------------------------- 1 | import type { Activity } from "@fedify/fedify"; 2 | import type { LogRecord } from "@logtape/logtape"; 3 | 4 | export interface ActivityEntry { 5 | timestamp: Temporal.Instant; 6 | request: Request; 7 | response?: Response; 8 | activity?: Activity; 9 | logs: LogRecord[]; 10 | } 11 | -------------------------------------------------------------------------------- /cli/inbox/rendercode.ts: -------------------------------------------------------------------------------- 1 | import type { Activity } from "@fedify/fedify"; 2 | import { getStatusText } from "@poppanator/http-constants"; 3 | import { getContextLoader } from "../docloader.ts"; 4 | 5 | export async function renderRequest(request: Request): Promise { 6 | request = request.clone(); 7 | const url = new URL(request.url); 8 | let code = `${request.method} ${url.pathname + url.search}\n`; 9 | for (const [key, value] of request.headers.entries()) { 10 | code += `${capitalize(key)}: ${value}\n`; 11 | } 12 | let body: string; 13 | try { 14 | body = await request.text(); 15 | } catch (_) { 16 | body = "[Failed to decode body; it may be binary.]"; 17 | } 18 | code += `\n${body}`; 19 | return code; 20 | } 21 | 22 | export async function renderResponse(response: Response): Promise { 23 | response = response.clone(); 24 | let code = `${response.status} ${ 25 | response.statusText === "" 26 | ? getStatusText(response.status) 27 | : response.statusText 28 | }\n`; 29 | for (const [key, value] of response.headers.entries()) { 30 | code += `${capitalize(key)}: ${value}\n`; 31 | } 32 | let body: string; 33 | try { 34 | body = await response.text(); 35 | } catch (_) { 36 | body = "[Failed to decode body; it may be binary.]"; 37 | } 38 | code += `\n${body}`; 39 | return code; 40 | } 41 | 42 | export async function renderRawActivity(request: Request): Promise { 43 | request = request.clone(); 44 | try { 45 | const activity = await request.json(); 46 | return JSON.stringify(activity, null, 2); 47 | } catch { 48 | return "[Failed to decode body; it may not be JSON.]"; 49 | } 50 | } 51 | 52 | export async function renderActivity( 53 | activity: Activity, 54 | expand: boolean = false, 55 | ): Promise { 56 | const contextLoader = await getContextLoader(); 57 | const jsonLd = await activity.toJsonLd({ 58 | contextLoader, 59 | format: expand ? "expand" : "compact", 60 | }); 61 | return JSON.stringify(jsonLd, null, 2); 62 | } 63 | 64 | function capitalize(name: string): string { 65 | return name.replace(/(^|-)./g, (match) => match.toUpperCase()); 66 | } 67 | -------------------------------------------------------------------------------- /cli/log.ts: -------------------------------------------------------------------------------- 1 | import { getFileSink } from "@logtape/file"; 2 | import { 3 | configure, 4 | getConsoleSink, 5 | type LogRecord, 6 | type Sink, 7 | } from "@logtape/logtape"; 8 | import { dirname } from "@std/path"; 9 | import { AsyncLocalStorage } from "node:async_hooks"; 10 | 11 | export interface RecordingSink extends Sink { 12 | startRecording(): void; 13 | stopRecording(): void; 14 | getRecords(): LogRecord[]; 15 | } 16 | 17 | export function getRecordingSink(): RecordingSink { 18 | let records: LogRecord[] = []; 19 | let recording = false; 20 | const sink: RecordingSink = (record: LogRecord) => { 21 | if (recording) records.push(record); 22 | }; 23 | sink.startRecording = () => { 24 | records = []; 25 | recording = true; 26 | }; 27 | sink.stopRecording = () => { 28 | recording = false; 29 | }; 30 | sink.getRecords = () => [...records]; 31 | return sink; 32 | } 33 | 34 | export const recordingSink = getRecordingSink(); 35 | 36 | export const logFile = Deno.env.get("FEDIFY_LOG_FILE"); 37 | if (logFile != null) { 38 | await Deno.mkdir(dirname(logFile), { recursive: true }); 39 | } 40 | 41 | await configure({ 42 | sinks: { 43 | console: getConsoleSink(), 44 | recording: recordingSink, 45 | file: logFile == null ? () => undefined : getFileSink(logFile), 46 | }, 47 | filters: {}, 48 | loggers: [ 49 | { 50 | category: "fedify", 51 | lowestLevel: "debug", 52 | sinks: ["recording", "file"], 53 | }, 54 | { 55 | category: ["logtape", "meta"], 56 | lowestLevel: "warning", 57 | sinks: ["console", "file"], 58 | }, 59 | ], 60 | contextLocalStorage: new AsyncLocalStorage(), 61 | reset: true, 62 | }); 63 | -------------------------------------------------------------------------------- /cli/npm/.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | *.tgz 3 | -------------------------------------------------------------------------------- /cli/npm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fedify/cli", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "files": [ 7 | "README.md", 8 | "install.mjs", 9 | "run.mjs" 10 | ], 11 | "bin": { 12 | "fedify": "./run.mjs" 13 | }, 14 | "scripts": { 15 | "postinstall": "node install.mjs" 16 | }, 17 | "engines": { 18 | "node": ">=18.0.0" 19 | }, 20 | "os": [ 21 | "darwin", 22 | "linux", 23 | "win32" 24 | ], 25 | "cpu": [ 26 | "x64", 27 | "arm64" 28 | ], 29 | "description": "CLI toolchain for Fedify and debugging ActivityPub", 30 | "keywords": [ 31 | "fedify", 32 | "activitypub", 33 | "cli", 34 | "fediverse" 35 | ], 36 | "homepage": "https://fedify.dev/cli", 37 | "bugs": { 38 | "url": "https://github.com/fedify-dev/fedify/issues" 39 | }, 40 | "license": "MIT", 41 | "author": { 42 | "name": "Hong Minhee", 43 | "email": "hong@minhee.org", 44 | "url": "https://hongminhee.org/" 45 | }, 46 | "funding": [ 47 | "https://github.com/sponsors/dahlia" 48 | ], 49 | "repository": { 50 | "type": "git", 51 | "url": "git+https://github.com/fedify-dev/fedify.git" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /cli/npm/run.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { spawnSync } from "node:child_process"; 3 | import { dirname, join } from "node:path"; 4 | import { fileURLToPath } from "node:url"; 5 | import { isFile, main as install } from "./install.mjs"; 6 | 7 | async function main() { 8 | const filename = fileURLToPath(import.meta.url); 9 | const dirName = dirname(filename); 10 | const binPath = join( 11 | dirName, 12 | "bin", 13 | process.platform === "win32" ? "fedify.exe" : "fedify", 14 | ); 15 | if (!await isFile(binPath)) await install(); 16 | const result = spawnSync(binPath, process.argv.slice(2), { 17 | stdio: "inherit", 18 | }); 19 | process.exit(result.status); 20 | } 21 | 22 | await main(); 23 | -------------------------------------------------------------------------------- /cli/scripts/check_version.ts: -------------------------------------------------------------------------------- 1 | import parentMetadata from "../../fedify/deno.json" with { type: "json" }; 2 | import metadata from "../deno.json" with { type: "json" }; 3 | 4 | if (metadata.version !== parentMetadata.version) { 5 | console.error( 6 | `Version mismatch: parent version ${parentMetadata.version} ` + 7 | `does not match child version ${metadata.version}`, 8 | ); 9 | Deno.exit(1); 10 | } 11 | -------------------------------------------------------------------------------- /cli/scripts/npm.ts: -------------------------------------------------------------------------------- 1 | import { which } from "jsr:@david/which@0.4.1"; 2 | import { dirname, join } from "jsr:@std/path@1.0.0"; 3 | import denoJson from "../deno.json" with { type: "json" }; 4 | import metadataTemplate from "../npm/package.json" with { type: "json" }; 5 | 6 | async function main() { 7 | const metadata = { 8 | ...metadataTemplate, 9 | name: denoJson.name, 10 | version: Deno.args.length < 1 ? denoJson.version : Deno.args[0], 11 | private: false, 12 | }; 13 | const tempDir = await Deno.makeTempDir(); 14 | console.debug("Working directory:", tempDir); 15 | await Deno.writeTextFile( 16 | join(tempDir, "package.json"), 17 | JSON.stringify(metadata), 18 | ); 19 | await Deno.copyFile( 20 | join(dirname(import.meta.dirname!), "npm", "install.mjs"), 21 | join(tempDir, "install.mjs"), 22 | ); 23 | await Deno.copyFile( 24 | join(dirname(import.meta.dirname!), "npm", "run.mjs"), 25 | join(tempDir, "run.mjs"), 26 | ); 27 | await Deno.copyFile( 28 | join(dirname(import.meta.dirname!), "README.md"), 29 | join(tempDir, "README.md"), 30 | ); 31 | const command = new Deno.Command(await which("npm") ?? "npm", { 32 | args: ["pack"], 33 | stdin: "inherit", 34 | stdout: "inherit", 35 | stderr: "inherit", 36 | cwd: tempDir, 37 | }); 38 | const result = await command.output(); 39 | if (!result.success) Deno.exit(result.code); 40 | for await (const entry of Deno.readDir(tempDir)) { 41 | if (entry.isFile && entry.name.endsWith(".tgz")) { 42 | await Deno.copyFile(join(tempDir, entry.name), entry.name); 43 | console.log(entry.name); 44 | } 45 | } 46 | } 47 | 48 | if (import.meta.main) await main(); 49 | -------------------------------------------------------------------------------- /cli/scripts/pack.ts: -------------------------------------------------------------------------------- 1 | import $ from "@david/dax"; 2 | import { dirname, join } from "@std/path"; 3 | import metadata from "../deno.json" with { type: "json" }; 4 | 5 | type OS = "linux" | "macos" | "windows"; 6 | type Arch = "x86_64" | "aarch64"; 7 | 8 | const triplets: Record>> = { 9 | linux: { 10 | x86_64: "x86_64-unknown-linux-gnu", 11 | aarch64: "aarch64-unknown-linux-gnu", 12 | }, 13 | macos: { 14 | x86_64: "x86_64-apple-darwin", 15 | aarch64: "aarch64-apple-darwin", 16 | }, 17 | windows: { 18 | x86_64: "x86_64-pc-windows-msvc", 19 | }, 20 | }; 21 | 22 | async function compile(os: OS, arch: Arch, into: string): Promise { 23 | const target = triplets[os][arch]; 24 | if (!target) { 25 | throw new Error(`Unsupported os/arch: ${os}/${arch}`); 26 | } 27 | await $`deno compile --allow-all --target=${target} --output=${into} ${ 28 | join(dirname(import.meta.dirname!), "mod.ts") 29 | }`; 30 | } 31 | 32 | async function pack(os: OS, arch: Arch): Promise { 33 | const dir = await Deno.makeTempDir(); 34 | await compile(os, arch, join(dir, "fedify")); 35 | await Deno.copyFile( 36 | join(dirname(import.meta.dirname!), "README.md"), 37 | join(dir, "README.md"), 38 | ); 39 | await Deno.copyFile( 40 | join(dirname(dirname(import.meta.dirname!)), "LICENSE"), 41 | join(dir, "LICENSE"), 42 | ); 43 | if (os === "windows") { 44 | const zipName = `fedify-cli-${metadata.version}-${os}-${arch}.zip`; 45 | await $`7z a ${zipName} fedify.exe README.md LICENSE`.cwd(dir); 46 | await Deno.copyFile(join(dir, zipName), zipName); 47 | } else { 48 | const tarName = `fedify-cli-${metadata.version}-${os}-${arch}.tar.xz`; 49 | await $`tar cfvJ ${tarName} fedify README.md LICENSE`.cwd(dir); 50 | await Deno.copyFile(join(dir, tarName), tarName); 51 | } 52 | } 53 | 54 | const promises: Promise[] = []; 55 | for (const osKey in triplets) { 56 | const os = osKey as OS; 57 | for (const arch in triplets[os]) { 58 | const promise = pack(os, arch as Arch); 59 | promises.push(promise); 60 | } 61 | } 62 | await Promise.all(promises); 63 | 64 | // cSpell: ignore cfvz 65 | -------------------------------------------------------------------------------- /cli/scripts/sync_version.ts: -------------------------------------------------------------------------------- 1 | import jsonPreserveIndent from "json-preserve-indent"; 2 | import metadata from "../../fedify/deno.json" with { type: "json" }; 3 | 4 | const denoJsonPath = `${import.meta.dirname}/../deno.json`; 5 | const denoJson = await Deno.readTextFile(denoJsonPath); 6 | const data = jsonPreserveIndent(denoJson); 7 | data.set("version", metadata.version); 8 | await Deno.writeTextFile(denoJsonPath, data.format()); 9 | -------------------------------------------------------------------------------- /cli/table.ts: -------------------------------------------------------------------------------- 1 | export const tableStyle = { 2 | top: "─", 3 | topMid: "┬", 4 | topLeft: "╭", 5 | topRight: "╮", 6 | bottom: "─", 7 | bottomMid: "┴", 8 | bottomLeft: "╰", 9 | bottomRight: "╯", 10 | left: "│", 11 | leftMid: "├", 12 | mid: "─", 13 | midMid: "┼", 14 | right: "│", 15 | rightMid: "┤", 16 | middle: "│", 17 | }; 18 | -------------------------------------------------------------------------------- /cli/tunnel.ts: -------------------------------------------------------------------------------- 1 | import { Command, EnumType } from "@cliffy/command"; 2 | import { openTunnel, type Tunnel } from "@hongminhee/localtunnel"; 3 | import ora from "ora"; 4 | 5 | const service = new EnumType(["localhost.run", "serveo.net"]); 6 | 7 | export const command = new Command() 8 | .type("service", service) 9 | .arguments("") 10 | .description( 11 | "Expose a local HTTP server to the public internet using a secure tunnel.\n\n" + 12 | "Note that the HTTP requests through the tunnel have X-Forwarded-* headers.", 13 | ) 14 | .option("-s, --service ", "The localtunnel service to use.") 15 | .action(async (options, port: number) => { 16 | const spinner = ora({ 17 | text: "Creating a secure tunnel...", 18 | discardStdin: false, 19 | }).start(); 20 | let tunnel: Tunnel; 21 | try { 22 | tunnel = await openTunnel({ port, service: options.service }); 23 | } catch { 24 | spinner.fail("Failed to create a secure tunnel."); 25 | Deno.exit(1); 26 | } 27 | spinner.succeed( 28 | `Your local server at ${port} is now publicly accessible:\n`, 29 | ); 30 | console.log(tunnel.url.href); 31 | console.error("\nPress ^C to close the tunnel."); 32 | Deno.addSignalListener("SIGINT", async () => { 33 | await tunnel.close(); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /cli/utils.ts: -------------------------------------------------------------------------------- 1 | import { highlight } from "cli-highlight"; 2 | 3 | export function printJson(json: unknown): void { 4 | const formatted = JSON.stringify(json, null, 2); 5 | console.log(highlight(formatted, { language: "json" })); 6 | } 7 | -------------------------------------------------------------------------------- /cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "words": [ 3 | "aarch64", 4 | "activitypub", 5 | "activitystreams", 6 | "aitertools", 7 | "apidoc", 8 | "authdocloader", 9 | "bccs", 10 | "biomejs", 11 | "Bluesky", 12 | "Bridgy", 13 | "btos", 14 | "callouts", 15 | "cfworker", 16 | "cfworkers", 17 | "codegen", 18 | "compactable", 19 | "cryptosuite", 20 | "decorrelated", 21 | "deflist", 22 | "Deno", 23 | "denokv", 24 | "denoland", 25 | "dereferenceable", 26 | "discoverability", 27 | "docloader", 28 | "draft-cavage", 29 | "eddsa", 30 | "fanout", 31 | "federatable", 32 | "Federatable", 33 | "fedi", 34 | "fedibird", 35 | "fedify", 36 | "fediverse", 37 | "followable", 38 | "Guppe", 39 | "halfyear", 40 | "highligher", 41 | "hongminhee", 42 | "hono", 43 | "httpsig", 44 | "hugoalh", 45 | "icojs", 46 | "instanceof", 47 | "interoperating", 48 | "jsonld", 49 | "keycache", 50 | "keypair", 51 | "langstr", 52 | "Lemmy", 53 | "litepub", 54 | "logtape", 55 | "lume", 56 | "lumocs", 57 | "metas", 58 | "microblog", 59 | "microblogging", 60 | "Minhee", 61 | "Misskey", 62 | "msvc", 63 | "multibase", 64 | "multicodec", 65 | "multikey", 66 | "multitenancy", 67 | "Nexkey", 68 | "nodeinfo", 69 | "phensley", 70 | "Pico", 71 | "Pixelfed", 72 | "PKCS", 73 | "Pleroma", 74 | "popd", 75 | "poppanator", 76 | "proto", 77 | "pushd", 78 | "pwsh", 79 | "redirections", 80 | "rels", 81 | "RSA-PKCS", 82 | "RSASSA-PKCS1", 83 | "runtimes", 84 | "setext", 85 | "shiki", 86 | "spki", 87 | "SSRF", 88 | "subproperty", 89 | "superproperty", 90 | "supertypes", 91 | "tempserver", 92 | "traceparent", 93 | "ts-nocheck", 94 | "tsdown", 95 | "twoslash", 96 | "typeof", 97 | "unfollow", 98 | "unfollowed", 99 | "unfollowing", 100 | "unfollows", 101 | "urlpattern", 102 | "uuidv7", 103 | "vitepress", 104 | "vtsls", 105 | "webfinger", 106 | "webp" 107 | ] 108 | } 109 | -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "workspace": [ 3 | "./fedify", 4 | "./cli", 5 | "./examples/blog", 6 | "./examples/cloudflare-workers", 7 | "./examples/hono-sample" 8 | ], 9 | "imports": { 10 | "@logtape/file": "jsr:@logtape/file@^0.11.0", 11 | "@logtape/logtape": "jsr:@logtape/logtape@^0.11.0", 12 | "@std/fs": "jsr:@std/fs@^1.0.3", 13 | "@std/path": "jsr:@std/path@^1.0.6", 14 | "json-preserve-indent": "npm:json-preserve-indent@^1.1.3", 15 | "preact": "npm:preact@10.19.6" 16 | }, 17 | "unstable": [ 18 | "fs", 19 | "kv", 20 | "process", 21 | "temporal" 22 | ], 23 | "tasks": { 24 | "codegen": "deno task -f @fedify/cli codegen", 25 | "check": { 26 | "command": "deno task -f @fedify/fedify check && deno task -f @fedify/cli check && deno task -f @fedify/blog check && deno task -f @fedify/hono-sample check", 27 | "dependencies": [ 28 | "check-version" 29 | ] 30 | }, 31 | "check-version": "deno task -f @fedify/fedify check-version", 32 | "test-all": "deno task -f @fedify/fedify test-all && deno task -f @fedify/cli check && deno task -f @fedify/blog check && deno task -f @fedify/hono-sample check", 33 | "publish": "deno task -f @fedify/fedify publish && deno task -f @fedify/cli publish", 34 | "cli": "deno task -f @fedify/cli run", 35 | "hooks:install": "deno run --allow-read=deno.json,.git/hooks/ --allow-write=.git/hooks/ jsr:@hongminhee/deno-task-hooks", 36 | "hooks:pre-commit": { 37 | "dependencies": [ 38 | "check" 39 | ] 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | .jsr-cache.json 2 | .vitepress/cache/ 3 | .vitepress/dist/ 4 | node_modules/ 5 | -------------------------------------------------------------------------------- /docs/.vitepress/.gitignore: -------------------------------------------------------------------------------- 1 | config.mts.timestamp-*.mjs 2 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | import TwoslashFloatingVue from "@shikijs/vitepress-twoslash/client"; 2 | import "virtual:group-icons.css"; 3 | import type { EnhanceAppContext } from "vitepress"; 4 | import Theme from "vitepress/theme"; 5 | 6 | import "@shikijs/vitepress-twoslash/style.css"; 7 | import "./style.css"; 8 | 9 | export default { 10 | extends: Theme, 11 | enhanceApp({ app }: EnhanceAppContext) { 12 | app.use(TwoslashFloatingVue); 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/style.css: -------------------------------------------------------------------------------- 1 | hr.footnotes-sep { 2 | margin-top: 48px; 3 | } 4 | 5 | section.footnotes:before { 6 | content: "Footnotes"; 7 | font-weight: bolder; 8 | } 9 | 10 | section.footnotes { 11 | font-size: smaller; 12 | } 13 | 14 | section.footnotes > ol { 15 | margin-top: 10px; 16 | padding-top: 0; 17 | } 18 | 19 | section.footnotes > ol > li { 20 | margin-top: 0; 21 | } 22 | 23 | section.footnotes > ol > li > p:first-child { 24 | margin-top: 0; 25 | padding-top: 0; 26 | } 27 | 28 | section.footnotes > ol > li > p:last-child { 29 | margin-bottom: 0; 30 | padding-bottom: 0; 31 | } 32 | 33 | main dl dd { 34 | margin-top: .25rem; 35 | margin-bottom: 1rem; 36 | } 37 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | Fedify docs 2 | =========== 3 | 4 | This directory contains the source files of the Fedify docs. The docs are 5 | written in Markdown format and are built with [VitePress]. 6 | 7 | In order to build the docs locally, you need to install [Bun] 8 | first. Then you can run the following commands (assuming you are in 9 | the *docs/* directory): 10 | 11 | ~~~~ bash 12 | bun install 13 | bun run dev 14 | ~~~~ 15 | 16 | Once the development server is running, you can open your browser and navigate 17 | to *http://localhost:5173/* to view the docs. 18 | 19 | [VitePress]: https://vitepress.dev/ 20 | [Bun]: https://bun.sh/ 21 | -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/cli/fedify-inbox-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/cli/fedify-inbox-web.png -------------------------------------------------------------------------------- /docs/cli/fedify-node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/cli/fedify-node.png -------------------------------------------------------------------------------- /docs/contribute.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/manual/pragmatics/mastodon-automated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/manual/pragmatics/mastodon-automated.png -------------------------------------------------------------------------------- /docs/manual/pragmatics/mastodon-avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/manual/pragmatics/mastodon-avatar.png -------------------------------------------------------------------------------- /docs/manual/pragmatics/mastodon-custom-fields.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/manual/pragmatics/mastodon-custom-fields.png -------------------------------------------------------------------------------- /docs/manual/pragmatics/mastodon-followers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/manual/pragmatics/mastodon-followers.png -------------------------------------------------------------------------------- /docs/manual/pragmatics/mastodon-following.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/manual/pragmatics/mastodon-following.png -------------------------------------------------------------------------------- /docs/manual/pragmatics/mastodon-group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/manual/pragmatics/mastodon-group.png -------------------------------------------------------------------------------- /docs/manual/pragmatics/mastodon-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/manual/pragmatics/mastodon-header.png -------------------------------------------------------------------------------- /docs/manual/pragmatics/mastodon-lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/manual/pragmatics/mastodon-lock.png -------------------------------------------------------------------------------- /docs/manual/pragmatics/mastodon-memorial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/manual/pragmatics/mastodon-memorial.png -------------------------------------------------------------------------------- /docs/manual/pragmatics/mastodon-person.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/manual/pragmatics/mastodon-person.png -------------------------------------------------------------------------------- /docs/manual/pragmatics/mastodon-suspended.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/manual/pragmatics/mastodon-suspended.png -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@braintree/sanitize-url": "^7.1.1", 4 | "@cloudflare/workers-types": "4.20250529.0", 5 | "@deno/kv": "^0.8.4", 6 | "@fedify/amqp": "^0.2.0", 7 | "@fedify/fedify": "1.6.1-pr.242.863", 8 | "@fedify/postgres": "^0.3.0", 9 | "@fedify/redis": "^0.4.0", 10 | "@hono/node-server": "^1.13.7", 11 | "@js-temporal/polyfill": "^0.5.1", 12 | "@logtape/file": "^0.10.0", 13 | "@logtape/logtape": "^0.10.0", 14 | "@opentelemetry/exporter-trace-otlp-proto": "^0.57.0", 15 | "@opentelemetry/sdk-node": "^0.57.0", 16 | "@sentry/node": "^8.47.0", 17 | "@shikijs/vitepress-twoslash": "^1.24.4", 18 | "@teidesu/deno-types": "^2.1.4", 19 | "@types/amqplib": "0.10.6", 20 | "@types/better-sqlite3": "^7.6.12", 21 | "@types/bun": "^1.1.14", 22 | "@types/node": "^22.15.21", 23 | "amqplib": "^0.10.5", 24 | "dayjs": "^1.11.13", 25 | "hono": "^4.6.14", 26 | "ioredis": "^5.4.2", 27 | "markdown-it-abbr": "^2.0.0", 28 | "markdown-it-deflist": "^3.0.0", 29 | "markdown-it-footnote": "^4.0.0", 30 | "markdown-it-jsr-ref": "0.4.1", 31 | "mermaid": "^11.4.1", 32 | "postgres": "^3.4.5", 33 | "stringify-entities": "^4.0.4", 34 | "typescript": "^5.8.3", 35 | "vitepress": "^1.5.0", 36 | "vitepress-plugin-group-icons": "^1.3.5", 37 | "vitepress-plugin-llms": "^1.1.0", 38 | "vitepress-plugin-mermaid": "^2.0.17", 39 | "x-forwarded-fetch": "^0.2.0" 40 | }, 41 | "scripts": { 42 | "dev": "vitepress dev", 43 | "build": "vitepress build", 44 | "preview": "vitepress preview" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /docs/public/favicon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/public/favicon-192x192.png -------------------------------------------------------------------------------- /docs/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/public/favicon-32x32.png -------------------------------------------------------------------------------- /docs/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/public/logo.png -------------------------------------------------------------------------------- /docs/security.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/sponsors.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/tutorial.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Tutorial 4 | ======== 5 | 6 | See [*Learning the basics of Fedify*](./tutorial/basics.md) instead. 7 | -------------------------------------------------------------------------------- /docs/tutorial/basics/search-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/basics/search-result.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/404.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/academy-compose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/academy-compose.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/academy-notifications.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/academy-notifications.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/academy-profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/academy-profile.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/academy-search-results-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/academy-search-results-2.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/academy-search-results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/academy-search-results.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/academy-timeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/academy-timeline.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/academy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/academy.jpg -------------------------------------------------------------------------------- /docs/tutorial/microblog/account-creation-page-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/account-creation-page-2.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/account-creation-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/account-creation-page.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/activity-log-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/activity-log-2.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/activity-log-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/activity-log-3.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/activity-log-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/activity-log-4.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/activity-log-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/activity-log-5.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/activity-log-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/activity-log-6.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/activity-log-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/activity-log-7.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/activity-log-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/activity-log-8.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/activity-log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/activity-log.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/followers-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/followers-list.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/following-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/following-list.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/home-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/home-2.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/home-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/home-3.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/home-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/home-4.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/home-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/home-5.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/home-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/home-6.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/home.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/post-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/post-page.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/profile-page-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/profile-page-2.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/profile-page-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/profile-page-3.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/profile-page-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/profile-page-4.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/profile-page-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/profile-page-5.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/profile-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/profile-page.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/remote-profile-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/remote-profile-2.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/remote-profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/remote-profile.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/search-results-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/search-results-2.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/search-results-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/search-results-3.png -------------------------------------------------------------------------------- /docs/tutorial/microblog/search-results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fedify-dev/fedify/c7fc12bbd4459313a71dabceb27cb0ea2e5cf2e5/docs/tutorial/microblog/search-results.png -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | Fedify examples 2 | =============== 3 | 4 | This directory contains example applications built with the Fedify framework. 5 | Currently, there is only few examples, but more examples will be 6 | added in the future.[^1] 7 | 8 | - [Actor lookup CLI](./actor-lookup-cli/) 9 | - [Federated single-user blog](./blog/) 10 | - [Federated microblog](https://github.com/fedify-dev/microblog), which is 11 | a final result of 12 | the [Creating a microblog](https://fedify.dev/tutorial/microblog) tutorial 13 | - [Fedi badge](https://github.com/dahlia/fedi-badge) 14 | - [Ghost's ActivityPub server](https://github.com/TryGhost/ActivityPub) 15 | - [Hollo: a federated single-user microblogging 16 | software](https://github.com/fedify-dev/hollo) 17 | - [Hono integration sample](./hono-sample/) 18 | - [Fedify–Express integration example](./express/) 19 | - [Fedify–Next.js 14 integration example](./next14-app-router/) 20 | - [Fedify–Next.js 15 integration example](./next15-app-router/) 21 | - [Fedify on Cloudflare Workers example](./cloudflare-workers/) 22 | 23 | [^1]: Contributions are welcome! If you have built an application with the 24 | Fedify framework and want to share it with others, please consider adding 25 | it to this list by sending a pull request. 26 | -------------------------------------------------------------------------------- /examples/actor-lookup-cli/README.md: -------------------------------------------------------------------------------- 1 | actor-lookup-cli 2 | ================ 3 | 4 | This example is a simple CLI program that looks up an actor by their fediverse 5 | handle (e.g. *@user@host*) and prints out their name, bio, stats, etc. It uses 6 | Fedify as a client library of ActivityPub, not as a server framework here. 7 | 8 | 9 | Usage 10 | ----- 11 | 12 | ~~~~ sh 13 | deno task codegen # At very first time only 14 | deno run -A ./main.ts @fedify@hollo.social 15 | ~~~~ 16 | -------------------------------------------------------------------------------- /examples/blog/.gitignore: -------------------------------------------------------------------------------- 1 | # dotenv environment variable files 2 | .env 3 | .env.development.local 4 | .env.test.local 5 | .env.production.local 6 | .env.local 7 | 8 | # Fresh build directory 9 | _fresh/ 10 | # Log files 11 | log.jsonl 12 | # npm dependencies 13 | node_modules/ 14 | -------------------------------------------------------------------------------- /examples/blog/components/Comment.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | type Comment as CommentModel, 3 | getContentHtml, 4 | } from "../models/comment.ts"; 5 | 6 | interface CommentProps { 7 | comment: CommentModel; 8 | } 9 | 10 | export default function Comment({ comment }: CommentProps) { 11 | return ( 12 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /examples/blog/components/Post.tsx: -------------------------------------------------------------------------------- 1 | import { getContentHtml, type Post as PostModel } from "../models/post.ts"; 2 | 3 | interface PostProps { 4 | post: PostModel; 5 | } 6 | 7 | export default function Post({ post }: PostProps) { 8 | return ( 9 |
10 |
11 |
12 |

13 | {post.title} 14 |

15 |

16 | 19 |

20 |
21 |
22 |
23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /examples/blog/components/PostForm.tsx: -------------------------------------------------------------------------------- 1 | import { JSX } from "preact"; 2 | 3 | export interface PostFormProps { 4 | error?: { 5 | title?: string; 6 | content?: string; 7 | password?: string; 8 | }; 9 | defaultValues?: { 10 | title?: string; 11 | content?: string; 12 | }; 13 | } 14 | 15 | export function PostForm( 16 | attrs: PostFormProps & JSX.HTMLAttributes, 17 | ) { 18 | const { error, defaultValues } = attrs; 19 | return ( 20 |
21 |
22 | 23 | 31 | {error?.title && ( 32 | 33 | {error.title} 34 | 35 | )} 36 |
37 |
38 | 39 |