@versia/federation@0.1.4Built and signed on GitHub ActionsBuilt and signed on GitHub Actions
Federation types, validators and cryptography for Versia server implementations.
@versia/federation
Federation types, validators and cryptography for Versia server implementations.
Efficiency
The built output of the package is not even 200 KB
in size, making it a lightweight and efficient solution for your Versia needs. Installing the package adds around 5 MB
to your node_modules
folder, but this does not affect the final bundle size.
Compilation (bundling/minifying) time is a few seconds, almost all of which is spent on type-checking. The actual compilation time is less than a tenth of a second.
Usage
Federation
Validation
Zod is used to validate and parse the objects. All Versia entities are already written for you.
import { EntityValidator, type ValidationError } from "@versia/federation"; const validator = new EntityValidator(); try { // Will throw an error when the object is invalid, otherwise return the correct object const invalidNote = await validator.Note({ // This is invalid type: "Note", content: 123, }); } catch (error) { // ToString returns the human-friendly error message sendUser((error as ValidationError).toString()); } // Types are also included for TypeScript users that don't use the extracted ones import type { Note } from "@versia/federation/types"; const validNoteObject: Note = { type: "Note", // ... }; const validNote = await validator.Note(validNoteObject); // validNote is still the same as noteObject
Your editor's IntelliSense should provide you with every method and property available, which all match the Versia specification names.
Requester
A FederationRequester
class is provided to make requests to a remote server. It sets the correct headers and has multiple methods to make requesters easier.
import { FederationRequester, SignatureConstructor } from "@versia/federation"; const requester = new FederationRequester( new URL("https://example.com"), new SignatureConstructor(privateKey, keyId), ); const { data, ok } = await requester.get<User>("/users/1"); if (!ok) { console.error(data); } console.log(data); // Do a WebFinger request const userProfileUri = await requester.webFinger("banana");
Validation Helper
RequestParserHandler
is a class to parse the body of a request and call the appropriate callback. It is a helper for the EntityValidator
class.
const body = { ... }; const validator = new EntityValidator(); const parser = new RequestParserHandler(body, validator); // Throws an error if the object is invalid // Same error as above await parser.parseBody({ note: (note) => { // If the object is a Note, this will be called console.log(note); }, follow: (follow) => { // If the object is a Follow, this will be called console.log(follow); }, ... });
Cryptography
The cryptography module provides two main classes: SignatureConstructor
and SignatureValidator
. These classes are used to construct and validate signatures for requests, respectively.
SignatureConstructor
The SignatureConstructor
class is used to construct a signature for a request. It can be instantiated with a private key and a key ID:
const privateKey = // CryptoKey const keyId = "https://example.com/users/6a18f2c3-120e-4949-bda4-2aa4c8264d51"; const constructor = new SignatureConstructor(privateKey, keyId);
Alternatively, you can create a SignatureConstructor
instance from a base64-encoded private key:
const privateKey = "base64PrivateKey"; const keyId = "https://example.com/users/6a18f2c3-120e-4949-bda4-2aa4c8264d51"; const constructor = await SignatureConstructor.fromStringKey(privateKey, keyId);
The sign
method is used to sign a request:
const request = new Request(); // Returns a Request object with Signature and Date set const signature = await constructor.sign(request); // Alternatively // Returns a Header object with Signature and Date set const signature = await constructor.sign(date, method, url, body);
SignatureValidator
The SignatureValidator
class is used to validate the signature of a request. It can be instantiated with a public key:
const publicKey = // CryptoKey const validator = new SignatureValidator(publicKey);
Alternatively, you can create a SignatureValidator
instance from a base64-encoded public key:
const publicKey = "base64PublicKey"; const validator = await SignatureValidator.fromStringKey(publicKey);
The validate
method is used to validate a request or signature:
const request = new Request(); // Returns boolean or TypeError if data is invalid const isValid = await validator.validate(request); // Alternatively // Returns boolean or TypeError if data is invalid const isValid = await validator.validate(signature, date, method, url, body);
Please note that these classes require the WebCrypto API and Ed25519 support in the environment. WebCrypto support is automatically checked, but Ed25519 cannot be as far as I know.
Getting Started
Prerequisites
For Usage
See the Compatibility section for the supported environments. Any package manager can be used to install the packages.
For Development
- Bun version
1.1.8
or higher. - Either the Linux or macOS operating systems. (Windows will work, but is not officially supported.)
Compatibility
This library is built for JavaScript runtimes with the support for:
- ES Modules
- ECMAScript 2020
- (only required for cryptography) Ed25519 cryptography in the WebCrypto API
Runtimes
- Node.js: 14.0+ is the minimum (18.0+ for cryptography), but only Node.js 20.0+ (LTS) is officially supported.
- Deno: Support is unknown. 1.0+ is expected to work.
- Bun: Bun 1.1.8 is the minimum-supported version. As Bun is rapidly evolving, this may change. Previous versions may also work.
Browsers
Consequently, this library is compatible without any bundling in the following browser versions:
- Chrome: 80+
- Edge: 80+
- Firefox: 74+
- Safari: 13.1+
- Opera: 67+
- Internet Explorer: None
Cryptography functions are supported in the following browsers:
- Safari: 17.0+
- Chrome: 113.0+ with
#enable-experimental-web-platform-features
enabled
If you are targeting older browsers, please don't, you are doing yourself a disservice.
Transpilation to non-ES Module environments is not officially supported, but should be simple with the use of a bundler like Parcel or Rollup.
Installation
Package is distributed as a scoped package on the NPM registry and JSR.
We strongly recommend using JSR over NPM for all your packages that are available on it.
# NPM version deno add npm:@versia/federation # For Deno npm install @versia/federation # For NPM yarn add @versia/federation # For Yarn pnpm add @versia/federation # For PNPM bun add @versia/federation # For Bun # JSR version deno add @versia/federation # For Deno npx jsr add @versia/federation # For JSR yarn dlx jsr add @versia/federation # For Yarn pnpm dlx jsr add @versia/federation # For PNPM bunx jsr add @versia/federation # For Bun
From Source
If you want to install from source, you can clone this repository and run the following commands:
bun install # Install dependencies bun run build # Build the packages
The built package will be in the federation/dist
folder.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
Projects
- Bun: Thanks to the Bun team for creating an amazing JavaScript runtime.
- TypeScript: TypeScript is the backbone of this project.
- Node.js: Node.js created the idea of JavaScript on the server.
People
- April John: Creator and maintainer of the Versia Server ActivityPub bridge.
Add Package
deno add jsr:@versia/federation
Import symbol
import * as federation from "@versia/federation";
---- OR ----
Import directly with a jsr specifier
import * as federation from "jsr:@versia/federation";
Add Package
npx jsr add @versia/federation
Import symbol
import * as federation from "@versia/federation";
Add Package
yarn dlx jsr add @versia/federation
Import symbol
import * as federation from "@versia/federation";
Add Package
pnpm dlx jsr add @versia/federation
Import symbol
import * as federation from "@versia/federation";
Add Package
bunx jsr add @versia/federation
Import symbol
import * as federation from "@versia/federation";