Loading cli/mod.ts +3 −1 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ import { logFile, recordingSink } from "./log.ts"; import { command as lookup } from "./lookup.ts"; import { command as node } from "./node.ts"; import { command as tunnel } from "./tunnel.ts"; import { command as webfinger } from "./webfinger.ts"; const command = new Command() .name("fedify") Loading Loading @@ -65,7 +66,8 @@ const command = new Command() .command("node", node) .command("tunnel", tunnel) .command("completions", new CompletionsCommand()) .command("help", new HelpCommand().global()); .command("help", new HelpCommand().global()) .command("webfinger", webfinger); if (import.meta.main) { await command.parse(Deno.args); Loading cli/webfinger.ts 0 → 100644 +89 −0 Original line number Diff line number Diff line import { Command } from "@cliffy/command"; import { lookupWebFinger } from "@fedify/fedify/webfinger"; import ora from "ora"; import { printJson } from "./utils.ts"; export const command = new Command() .arguments("<handle:string>") .description( "Lookup a WebFinger resource by handle. The argument can be multiple.", ) .option( "-a, --user-agent <userAgent:string>", "The user agent to use for the request.", ) .option( "-p, --allow-private-address", "Allow private IP addresses in the URL.", ) .action(async (options, handle: string) => { try { const url = convertHandleToUrl(handle); // Convert handle to URL const spinner = ora({ // Create a spinner for the lookup process text: `Looking up WebFinger for ${handle}`, discardStdin: false, }).start(); const webFinger = await lookupWebFinger(url, options); // Look up WebFinger if (webFinger == null) { // If no WebFinger found, spinner.fail(`No WebFinger found for ${handle}`); // fail the spinner } spinner.succeed(`WebFinger found for ${handle}:`); // Succeed the spinner printJson(webFinger); // Print the WebFinger } catch (error) { if (error instanceof InvalidHandleError) { // If the handle format is invalid, console.error(`Invalid handle format: ${error.handle}`); // log error message with handle } else { console.error( // For other errors, log the error message `Error looking up WebFinger for ${handle}:`, error, ); } } }); /** * Regular expression to match a handle in the format `@username@domain`. * The username can contain any characters except `@`. * The domain must be match with `[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}`. */ const HANDLE_REGEX = /^@?([^@]+)@([-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6})$/; /** * Custom error class for invalid handle formats. * @param handle The invalid handle that caused the error. * @extends {Error} */ class InvalidHandleError extends Error { constructor(public handle: string) { super(`Invalid handle format: ${handle}`); this.name = "InvalidHandleError"; } } /** * Converts a handle in the format `@username@domain` to a URL. * The resulting URL will be in the format `https://domain/@username`. * @param handle The handle to convert, in the format `@username@domain`. * @returns A URL object representing the handle. * @throws {Error} If the handle format is invalid. * @example * ```ts * const url = convertHandleToUrl("@username@domain.com"); * console.log(url.toString()); // "https://domain.com/@username" * ``` */ function convertHandleToUrl(handle: string): URL { const match = handle.match(HANDLE_REGEX); // Match the handle format if (!match) { // If the handle does not match, throw new InvalidHandleError(handle); // throw `Invalid handle format` error } const [, username, domain] = match; // Extract username and domain from the match if (!username || !domain) { // If username or domain is empty, throw new InvalidHandleError(handle); // throw `Invalid handle format` error } return new URL(`https://${domain}/@${username}`); // Create a URL object with the domain and username } Loading
cli/mod.ts +3 −1 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ import { logFile, recordingSink } from "./log.ts"; import { command as lookup } from "./lookup.ts"; import { command as node } from "./node.ts"; import { command as tunnel } from "./tunnel.ts"; import { command as webfinger } from "./webfinger.ts"; const command = new Command() .name("fedify") Loading Loading @@ -65,7 +66,8 @@ const command = new Command() .command("node", node) .command("tunnel", tunnel) .command("completions", new CompletionsCommand()) .command("help", new HelpCommand().global()); .command("help", new HelpCommand().global()) .command("webfinger", webfinger); if (import.meta.main) { await command.parse(Deno.args); Loading
cli/webfinger.ts 0 → 100644 +89 −0 Original line number Diff line number Diff line import { Command } from "@cliffy/command"; import { lookupWebFinger } from "@fedify/fedify/webfinger"; import ora from "ora"; import { printJson } from "./utils.ts"; export const command = new Command() .arguments("<handle:string>") .description( "Lookup a WebFinger resource by handle. The argument can be multiple.", ) .option( "-a, --user-agent <userAgent:string>", "The user agent to use for the request.", ) .option( "-p, --allow-private-address", "Allow private IP addresses in the URL.", ) .action(async (options, handle: string) => { try { const url = convertHandleToUrl(handle); // Convert handle to URL const spinner = ora({ // Create a spinner for the lookup process text: `Looking up WebFinger for ${handle}`, discardStdin: false, }).start(); const webFinger = await lookupWebFinger(url, options); // Look up WebFinger if (webFinger == null) { // If no WebFinger found, spinner.fail(`No WebFinger found for ${handle}`); // fail the spinner } spinner.succeed(`WebFinger found for ${handle}:`); // Succeed the spinner printJson(webFinger); // Print the WebFinger } catch (error) { if (error instanceof InvalidHandleError) { // If the handle format is invalid, console.error(`Invalid handle format: ${error.handle}`); // log error message with handle } else { console.error( // For other errors, log the error message `Error looking up WebFinger for ${handle}:`, error, ); } } }); /** * Regular expression to match a handle in the format `@username@domain`. * The username can contain any characters except `@`. * The domain must be match with `[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}`. */ const HANDLE_REGEX = /^@?([^@]+)@([-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6})$/; /** * Custom error class for invalid handle formats. * @param handle The invalid handle that caused the error. * @extends {Error} */ class InvalidHandleError extends Error { constructor(public handle: string) { super(`Invalid handle format: ${handle}`); this.name = "InvalidHandleError"; } } /** * Converts a handle in the format `@username@domain` to a URL. * The resulting URL will be in the format `https://domain/@username`. * @param handle The handle to convert, in the format `@username@domain`. * @returns A URL object representing the handle. * @throws {Error} If the handle format is invalid. * @example * ```ts * const url = convertHandleToUrl("@username@domain.com"); * console.log(url.toString()); // "https://domain.com/@username" * ``` */ function convertHandleToUrl(handle: string): URL { const match = handle.match(HANDLE_REGEX); // Match the handle format if (!match) { // If the handle does not match, throw new InvalidHandleError(handle); // throw `Invalid handle format` error } const [, username, domain] = match; // Extract username and domain from the match if (!username || !domain) { // If username or domain is empty, throw new InvalidHandleError(handle); // throw `Invalid handle format` error } return new URL(`https://${domain}/@${username}`); // Create a URL object with the domain and username }