@fedify/cli 1.8.11 → 2.0.0-dev.1757

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.
Files changed (130) hide show
  1. package/deno.json +72 -0
  2. package/dist/cache.js +17 -0
  3. package/dist/deno.js +72 -0
  4. package/dist/docloader.js +52 -0
  5. package/dist/globals.js +49 -0
  6. package/dist/imagerenderer.js +105 -0
  7. package/dist/inbox/rendercode.js +57 -0
  8. package/dist/inbox/view.js +508 -0
  9. package/dist/inbox.js +315 -0
  10. package/dist/init/action/configs.js +81 -0
  11. package/dist/init/action/deps.js +52 -0
  12. package/dist/init/action/dir.js +16 -0
  13. package/dist/init/action/env.js +13 -0
  14. package/dist/init/action/install.js +22 -0
  15. package/dist/init/action/mod.js +39 -0
  16. package/dist/init/action/notice.js +62 -0
  17. package/dist/init/action/patch.js +141 -0
  18. package/dist/init/action/precommand.js +23 -0
  19. package/dist/init/action/recommend.js +24 -0
  20. package/dist/init/action/set.js +31 -0
  21. package/dist/init/action/templates.js +57 -0
  22. package/dist/init/action/utils.js +50 -0
  23. package/dist/init/ask/dir.js +82 -0
  24. package/dist/init/ask/kv.js +33 -0
  25. package/dist/init/ask/mod.js +16 -0
  26. package/dist/init/ask/mq.js +33 -0
  27. package/dist/init/ask/pm.js +49 -0
  28. package/dist/init/ask/wf.js +29 -0
  29. package/dist/init/command.js +25 -0
  30. package/dist/init/const.js +31 -0
  31. package/dist/init/json/biome.js +24 -0
  32. package/dist/init/json/kv.js +53 -0
  33. package/dist/init/json/mq.js +72 -0
  34. package/dist/init/json/pm.js +44 -0
  35. package/dist/init/json/rt.js +39 -0
  36. package/dist/init/json/vscode-settings-for-deno.js +53 -0
  37. package/dist/init/json/vscode-settings.js +49 -0
  38. package/dist/init/lib.js +129 -0
  39. package/dist/init/mod.js +5 -0
  40. package/dist/init/webframeworks.js +133 -0
  41. package/dist/kv.bun.js +17 -0
  42. package/dist/kv.node.js +17 -0
  43. package/dist/log.js +52 -0
  44. package/dist/lookup.js +287 -0
  45. package/dist/mod.js +34 -0
  46. package/dist/nodeinfo.js +261 -0
  47. package/dist/table.js +24 -0
  48. package/dist/tempserver.js +71 -0
  49. package/dist/tunnel.js +21 -0
  50. package/dist/utils.js +67 -0
  51. package/dist/webfinger/action.js +44 -0
  52. package/dist/webfinger/command.js +20 -0
  53. package/dist/webfinger/error.js +47 -0
  54. package/dist/webfinger/lib.js +45 -0
  55. package/dist/webfinger/mod.js +5 -0
  56. package/package.json +64 -24
  57. package/scripts/pack.ts +64 -0
  58. package/src/cache.ts +17 -0
  59. package/src/docloader.ts +67 -0
  60. package/src/globals.ts +43 -0
  61. package/src/imagerenderer.ts +149 -0
  62. package/src/inbox/entry.ts +10 -0
  63. package/src/inbox/rendercode.ts +68 -0
  64. package/src/inbox/view.tsx +598 -0
  65. package/src/inbox.tsx +535 -0
  66. package/src/init/action/configs.ts +88 -0
  67. package/src/init/action/deps.ts +93 -0
  68. package/src/init/action/dir.ts +11 -0
  69. package/src/init/action/env.ts +14 -0
  70. package/src/init/action/install.ts +59 -0
  71. package/src/init/action/mod.ts +66 -0
  72. package/src/init/action/notice.ts +101 -0
  73. package/src/init/action/patch.ts +212 -0
  74. package/src/init/action/precommand.ts +22 -0
  75. package/src/init/action/recommend.ts +38 -0
  76. package/src/init/action/set.ts +78 -0
  77. package/src/init/action/templates.ts +95 -0
  78. package/src/init/action/utils.ts +64 -0
  79. package/src/init/ask/dir.ts +98 -0
  80. package/src/init/ask/kv.ts +39 -0
  81. package/src/init/ask/mod.ts +23 -0
  82. package/src/init/ask/mq.ts +37 -0
  83. package/src/init/ask/pm.ts +58 -0
  84. package/src/init/ask/wf.ts +27 -0
  85. package/src/init/command.ts +64 -0
  86. package/src/init/const.ts +4 -0
  87. package/src/init/json/biome.json +17 -0
  88. package/src/init/json/kv.json +39 -0
  89. package/src/init/json/mq.json +95 -0
  90. package/src/init/json/pm.json +47 -0
  91. package/src/init/json/rt.json +42 -0
  92. package/src/init/json/vscode-settings-for-deno.json +43 -0
  93. package/src/init/json/vscode-settings.json +41 -0
  94. package/src/init/lib.ts +220 -0
  95. package/src/init/mod.ts +2 -0
  96. package/src/init/templates/defaults/federation.ts.tpl +23 -0
  97. package/src/init/templates/defaults/logging.ts.tpl +23 -0
  98. package/src/init/templates/express/app.ts.tpl +16 -0
  99. package/src/init/templates/express/index.ts.tpl +6 -0
  100. package/src/init/templates/hono/app.tsx.tpl +14 -0
  101. package/src/init/templates/hono/index/bun.ts.tpl +10 -0
  102. package/src/init/templates/hono/index/deno.ts.tpl +13 -0
  103. package/src/init/templates/hono/index/node.ts.tpl +14 -0
  104. package/src/init/templates/next/middleware.ts.tpl +45 -0
  105. package/src/init/templates/nitro/nitro.config.ts.tpl +5 -0
  106. package/src/init/templates/nitro/server/error.ts.tpl +3 -0
  107. package/src/init/templates/nitro/server/middleware/federation.ts.tpl +8 -0
  108. package/src/init/types.ts +88 -0
  109. package/src/init/webframeworks.ts +151 -0
  110. package/src/kv.bun.ts +12 -0
  111. package/src/kv.node.ts +11 -0
  112. package/src/log.ts +64 -0
  113. package/src/lookup.test.ts +182 -0
  114. package/src/lookup.ts +558 -0
  115. package/src/mod.ts +45 -0
  116. package/src/nodeinfo.test.ts +229 -0
  117. package/src/nodeinfo.ts +447 -0
  118. package/src/table.ts +17 -0
  119. package/src/tempserver.ts +87 -0
  120. package/src/tunnel.ts +32 -0
  121. package/src/utils.ts +136 -0
  122. package/src/webfinger/action.ts +50 -0
  123. package/src/webfinger/command.ts +59 -0
  124. package/src/webfinger/error.ts +47 -0
  125. package/src/webfinger/lib.ts +37 -0
  126. package/src/webfinger/mod.test.ts +79 -0
  127. package/src/webfinger/mod.ts +2 -0
  128. package/tsdown.config.ts +24 -0
  129. package/src/install.mjs +0 -189
  130. package/src/run.mjs +0 -22
@@ -0,0 +1,87 @@
1
+ import { openTunnel } from "@hongminhee/localtunnel";
2
+ import { getLogger } from "@logtape/logtape";
3
+ import { serve } from "srvx";
4
+
5
+ const logger = getLogger(["fedify", "cli", "tempserver"]);
6
+
7
+ export type SpawnTemporaryServerOptions = {
8
+ noTunnel?: boolean;
9
+ };
10
+
11
+ export type TemporaryServer = {
12
+ url: URL;
13
+ close(): Promise<void>;
14
+ };
15
+
16
+ export async function spawnTemporaryServer(
17
+ fetch: (request: Request) => Promise<Response> | Response,
18
+ options: SpawnTemporaryServerOptions = {},
19
+ ): Promise<TemporaryServer> {
20
+ if (options.noTunnel) {
21
+ const server = serve({
22
+ port: 0,
23
+ hostname: "::",
24
+ fetch: fetch,
25
+ });
26
+ await server.ready();
27
+ const url = new URL(server.url!);
28
+ const port = url.port;
29
+ logger.debug("Temporary server is listening on port {port}.", {
30
+ port: port,
31
+ });
32
+
33
+ return {
34
+ url: new URL(`http://localhost:${port}`),
35
+ async close() {
36
+ await server.close();
37
+ },
38
+ };
39
+ }
40
+
41
+ const server = serve({
42
+ fetch: (request) => {
43
+ const url = new URL(request.url);
44
+ url.protocol = "https:";
45
+ request = new Request(url, {
46
+ method: request.method,
47
+ headers: request.headers,
48
+ body: request.method === "GET" || request.method === "HEAD"
49
+ ? null
50
+ : request.body,
51
+ referrer: request.referrer,
52
+ referrerPolicy: request.referrerPolicy,
53
+ mode: request.mode,
54
+ credentials: request.credentials,
55
+ cache: request.cache,
56
+ redirect: request.redirect,
57
+ integrity: request.integrity,
58
+ keepalive: request.keepalive,
59
+ signal: request.signal,
60
+ });
61
+
62
+ return new Response();
63
+ },
64
+ port: 0,
65
+ hostname: "::",
66
+ });
67
+
68
+ await server.ready();
69
+
70
+ const url = new URL(server.url!);
71
+ const port = url.port;
72
+
73
+ logger.debug("Temporary server is listening on port {port}.", { port });
74
+ const tun = await openTunnel({ port: parseInt(port) });
75
+ logger.debug(
76
+ "Temporary server is tunneled to {url}.",
77
+ { url: tun.url.href },
78
+ );
79
+
80
+ return {
81
+ url: tun.url,
82
+ async close() {
83
+ await server.close();
84
+ await tun.close();
85
+ },
86
+ };
87
+ }
package/src/tunnel.ts ADDED
@@ -0,0 +1,32 @@
1
+ import {
2
+ argument,
3
+ command,
4
+ constant,
5
+ type InferValue,
6
+ integer,
7
+ merge,
8
+ message,
9
+ object,
10
+ } from "@optique/core";
11
+ import { debugOption } from "./globals.ts";
12
+
13
+ export const tunnelCommand = command(
14
+ "tunnel",
15
+ merge(
16
+ object({
17
+ command: constant("tunnel"),
18
+ port: argument(integer({ metavar: "PORT", min: 0, max: 65_535 })),
19
+ }),
20
+ debugOption,
21
+ ),
22
+ {
23
+ description:
24
+ message`Expose a local HTTP server to the public internet using a secure tunnel.`,
25
+ },
26
+ );
27
+
28
+ export function runTunnel(
29
+ command: InferValue<typeof tunnelCommand>,
30
+ ) {
31
+ console.debug(command);
32
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,136 @@
1
+ import { isObject } from "@fxts/core";
2
+ import { Chalk } from "chalk";
3
+ import { highlight } from "cli-highlight";
4
+ import { toMerged } from "es-toolkit";
5
+ import { spawn } from "node:child_process";
6
+ import { writeFile } from "node:fs/promises";
7
+ import process from "node:process";
8
+ import util from "node:util";
9
+
10
+ export const colorEnabled: boolean = process.stdout.isTTY &&
11
+ !("NO_COLOR" in process.env && process.env.NO_COLOR !== "");
12
+
13
+ export const colors = new Chalk(colorEnabled ? {} : { level: 0 });
14
+
15
+ export function formatObject(
16
+ obj: unknown,
17
+ colors?: boolean,
18
+ json?: boolean,
19
+ ): string {
20
+ const enableColors = colors ?? colorEnabled;
21
+ if (!json) return util.inspect(obj, { colors: enableColors });
22
+ const formatted = JSON.stringify(obj, null, 2);
23
+ if (enableColors) {
24
+ return highlight(formatted, { language: "json" });
25
+ }
26
+ return formatted;
27
+ }
28
+
29
+ export const isPromise = <T>(a: unknown): a is Promise<T> =>
30
+ a instanceof Promise;
31
+
32
+ export function set<K extends PropertyKey, T extends object, S>(
33
+ key: K,
34
+ f: (value: T) => S,
35
+ ): (
36
+ obj: T,
37
+ ) => S extends Promise<infer U> ? Promise<T & { [P in K]: Awaited<U> }>
38
+ : T & { [P in K]: S } {
39
+ return ((obj) => {
40
+ const result = f(obj);
41
+ if (isPromise<S extends Promise<infer U> ? U : never>(result)) {
42
+ return result.then((value) => ({ ...obj, [key]: value })) as S extends
43
+ Promise<infer U> ? Promise<
44
+ T & { [P in K]: Awaited<U> }
45
+ >
46
+ : never;
47
+ }
48
+ return ({ ...obj, [key]: result }) as S extends Promise<infer _> ? never
49
+ : T & { [P in K]: S };
50
+ });
51
+ }
52
+
53
+ export const merge =
54
+ (source: Parameters<typeof toMerged>[1] = {}) =>
55
+ (target: Parameters<typeof toMerged>[0] = {}) => toMerged(target, source);
56
+
57
+ export const isNotFoundError = (e: unknown): e is { code: "ENOENT" } =>
58
+ isObject(e) &&
59
+ "code" in e &&
60
+ e.code === "ENOENT";
61
+
62
+ export const runSubCommand = <Opt extends Parameters<typeof spawn>[2]>(
63
+ command: string[],
64
+ options: Opt,
65
+ ): Promise<{
66
+ stdout: string;
67
+ stderr: string;
68
+ }> =>
69
+ new Promise((resolve, reject) => {
70
+ const child = spawn(command[0], command.slice(1), options);
71
+
72
+ let stdout = "";
73
+ let stderr = "";
74
+
75
+ child.stdout?.on("data", (data) => {
76
+ stdout += data.toString();
77
+ });
78
+ child.stderr?.on("data", (data) => {
79
+ stderr += data.toString();
80
+ });
81
+
82
+ child.on("close", () => {
83
+ resolve({
84
+ stdout: stdout.trim(),
85
+ stderr: stderr.trim(),
86
+ });
87
+ });
88
+
89
+ child.on("error", (error) => {
90
+ reject(error);
91
+ });
92
+ });
93
+
94
+ export type RequiredNotNull<T> = {
95
+ [P in keyof T]: NonNullable<T[P]>;
96
+ };
97
+
98
+ export const getCwd = () => process.cwd();
99
+
100
+ export const replace = (
101
+ pattern: string | RegExp,
102
+ replacement: string | ((substring: string, ...args: unknown[]) => string),
103
+ ) =>
104
+ (text: string): string => text.replace(pattern, replacement as string);
105
+
106
+ export const getOsType = () => process.platform;
107
+
108
+ export async function writeTextFile(
109
+ path: string,
110
+ content: string,
111
+ ): Promise<void> {
112
+ const encoder = new TextEncoder();
113
+ const data = encoder.encode(content);
114
+ return await writeFile(path, data);
115
+ }
116
+
117
+ export const resolveProps = async <T extends object>(obj: T): Promise<
118
+ { [P in keyof T]: Awaited<T[P]> }
119
+ > =>
120
+ Object.fromEntries(
121
+ await Array.fromAsync(
122
+ Object.entries(obj),
123
+ async ([k, v]) => [k, await v],
124
+ ),
125
+ ) as Promise<{ [P in keyof T]: Awaited<T[P]> }>;
126
+
127
+ export const formatJson = (obj: unknown) => JSON.stringify(obj, null, 2) + "\n";
128
+
129
+ export const notEmpty = <T extends string | { length: number }>(s: T) =>
130
+ s.length > 0;
131
+
132
+ export const notEmptyObj = <T extends Record<PropertyKey, never> | object>(
133
+ obj: T,
134
+ ): obj is Exclude<T, Record<PropertyKey, never>> => Object.keys(obj).length > 0;
135
+
136
+ export const exit = (code: number) => process.exit(code);
@@ -0,0 +1,50 @@
1
+ import type { ResourceDescriptor } from "@fedify/fedify";
2
+ import {
3
+ lookupWebFinger,
4
+ type LookupWebFingerOptions,
5
+ } from "@fedify/fedify/webfinger";
6
+ import { formatMessage, message } from "@optique/core/message";
7
+ import { print } from "@optique/run";
8
+ import ora from "ora";
9
+ import { formatObject } from "../utils.ts";
10
+ import type { WebFingerCommand } from "./command.ts";
11
+ import { getErrorMessage, NotFoundError } from "./error.ts";
12
+ import { convertUrlIfHandle } from "./lib.ts";
13
+
14
+ export default async function runWebFinger(
15
+ { command: _, resources, ...options }: WebFingerCommand,
16
+ ) {
17
+ await Array.fromAsync(
18
+ resources.map((resource) => ({ resource, ...options })),
19
+ spinnerWrapper(lookupSingleWebFinger),
20
+ );
21
+ }
22
+
23
+ export async function lookupSingleWebFinger<
24
+ T extends LookupWebFingerOptions & { resource: string },
25
+ >({ resource, ...options }: T): Promise<ResourceDescriptor> {
26
+ const url = convertUrlIfHandle(resource);
27
+ const webFinger = await lookupWebFinger(url, options) ??
28
+ new NotFoundError(resource).throw();
29
+ return webFinger;
30
+ }
31
+
32
+ function spinnerWrapper<F extends typeof lookupSingleWebFinger>(
33
+ func: (...args: Parameters<F>) => ReturnType<F>,
34
+ ) {
35
+ return async (...args: Parameters<F>) => {
36
+ const spinner = ora({
37
+ text: `Looking up WebFinger for ${args[0]}`,
38
+ discardStdin: false,
39
+ }).start();
40
+ try {
41
+ const result = await func(...args);
42
+ spinner.succeed(
43
+ formatMessage(message`WebFinger found for ${args[0].resource}:`),
44
+ );
45
+ print([{ type: "text", text: formatObject(result) }]);
46
+ } catch (error) {
47
+ spinner.fail(formatMessage(getErrorMessage(args[0].resource, error)));
48
+ }
49
+ };
50
+ }
@@ -0,0 +1,59 @@
1
+ import {
2
+ argument,
3
+ command,
4
+ constant,
5
+ flag,
6
+ type InferValue,
7
+ integer,
8
+ merge,
9
+ message,
10
+ multiple,
11
+ object,
12
+ option,
13
+ optional,
14
+ string,
15
+ withDefault,
16
+ } from "@optique/core";
17
+ import { debugOption } from "../globals.ts";
18
+
19
+ const userAgent = optional(option(
20
+ "-u",
21
+ "--user-agent",
22
+ string({ metavar: "USER_AGENT" }),
23
+ { description: message`The custom User-Agent header value.` },
24
+ ));
25
+
26
+ const allowPrivateAddresses = optional(flag("-p", "--allow-private-address", {
27
+ description: message`Allow private IP addresses in the URL.`,
28
+ }));
29
+
30
+ const maxRedirection = withDefault(
31
+ option(
32
+ "--max-redirection",
33
+ integer({ min: 0 }),
34
+ { description: message`Maximum number of redirections to follow.` },
35
+ ),
36
+ 5,
37
+ );
38
+
39
+ export const webFingerCommand = command(
40
+ "webfinger",
41
+ merge(
42
+ object({
43
+ command: constant("webfinger"),
44
+ resources: multiple(argument(string({ metavar: "RESOURCE" }), {
45
+ description: message`WebFinger resource(s) to look up.`,
46
+ })),
47
+ userAgent,
48
+ allowPrivateAddresses,
49
+ maxRedirection,
50
+ }),
51
+ debugOption,
52
+ ),
53
+ {
54
+ description:
55
+ message`Look up WebFinger resources. The argument can be multiple.`,
56
+ },
57
+ );
58
+
59
+ export type WebFingerCommand = InferValue<typeof webFingerCommand>;
@@ -0,0 +1,47 @@
1
+ import { type Message, message } from "@optique/core";
2
+
3
+ /**
4
+ * Generates a user-friendly error message based on the type of error
5
+ * encountered during WebFinger lookup.
6
+ * @param {string} resource The resource being looked up.
7
+ * @param {unknown} error The error encountered.
8
+ * @returns {string} A descriptive error message.
9
+ */
10
+ export const getErrorMessage = (resource: string, error: unknown): Message =>
11
+ error instanceof InvalidHandleError
12
+ ? message`Invalid handle format: ${error.handle}`
13
+ : error instanceof NotFoundError
14
+ ? message`Resource not found: ${error.resource}`
15
+ : error instanceof Error
16
+ ? message`Failed to look up WebFinger for ${resource}: ${error.message}`
17
+ : message`Failed to look up WebFinger for ${resource}: ${String(error)}`;
18
+
19
+ /**
20
+ * Custom error class for invalid handle formats.
21
+ * @param {string} handle The invalid handle that caused the error.
22
+ * @extends {Error}
23
+ */
24
+ export class InvalidHandleError extends Error {
25
+ constructor(public handle: string) {
26
+ super(`Invalid handle format: ${handle}`);
27
+ this.name = "InvalidHandleError";
28
+ }
29
+ throw(): never {
30
+ throw this;
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Custom error class for not found resources.
36
+ * @param {string} resource The resource that was not found.
37
+ * @extends {Error}
38
+ */
39
+ export class NotFoundError extends Error {
40
+ constructor(public resource: string) {
41
+ super(`Resource not found: ${resource}`);
42
+ this.name = "NotFoundError";
43
+ }
44
+ throw(): never {
45
+ throw this;
46
+ }
47
+ }
@@ -0,0 +1,37 @@
1
+ import { toAcctUrl } from "@fedify/fedify";
2
+ import { getLogger } from "@logtape/logtape";
3
+ import { InvalidHandleError } from "./error.ts";
4
+
5
+ export const logger = getLogger(["fedify", "cli", "webfinger"]);
6
+
7
+ /**
8
+ * Converts a handle or URL to a URL object.
9
+ * If the input is a valid URL, it returns the URL object.
10
+ * If the input is a handle in the format `@username@domain`, it converts it to a URL.
11
+ * @param handleOrUrl The handle or URL to convert.
12
+ * @returns A URL object representing the handle or URL.
13
+ */
14
+ export function convertUrlIfHandle(handleOrUrl: string): URL {
15
+ try {
16
+ return new URL(handleOrUrl); // Try to convert the input to a URL
17
+ } catch {
18
+ return convertHandleToUrl(handleOrUrl); // If it fails, treat it as a handle
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Converts a handle in the format `@username@domain` to a URL.
24
+ * The resulting URL will be in the format `https://domain/@username`.
25
+ * @param handle The handle to convert, in the format `@username@domain`.
26
+ * @returns A URL object representing the handle.
27
+ * @throws {Error} If the handle format is invalid.
28
+ * @example
29
+ * ```ts
30
+ * const url = convertHandleToUrl("@username@domain.com");
31
+ * console.log(url.toString()); // "https://domain.com/@username"
32
+ * ```
33
+ */
34
+ export function convertHandleToUrl(handle: string): URL {
35
+ return toAcctUrl(handle) ?? // Convert the handle to a URL
36
+ new InvalidHandleError(handle).throw(); // or throw an error if invalid
37
+ }
@@ -0,0 +1,79 @@
1
+ import { parse } from "@optique/core/parser";
2
+ import assert from "node:assert/strict";
3
+ import test from "node:test";
4
+ import { lookupSingleWebFinger } from "./action.ts";
5
+ import { webFingerCommand } from "./command.ts";
6
+
7
+ const COMMAND = "webfinger";
8
+ const USER_AGENT = "MyUserAgent/1.0";
9
+ const RESOURCES = [
10
+ "@hongminhee@hackers.pub",
11
+ "@fedify@hollo.social",
12
+ ];
13
+ const ALIASES = [
14
+ "https://hackers.pub/ap/actors/019382d3-63d7-7cf7-86e8-91e2551c306c",
15
+ "https://hollo.social/@fedify",
16
+ ];
17
+
18
+ test("Test webFingerCommand", () => {
19
+ // Resources only
20
+ const argsWithResourcesOnly = [COMMAND, ...RESOURCES];
21
+ assert.deepEqual(
22
+ parse(webFingerCommand, argsWithResourcesOnly),
23
+ {
24
+ success: true,
25
+ value: {
26
+ debug: false,
27
+ command: COMMAND,
28
+ resources: RESOURCES,
29
+ allowPrivateAddresses: undefined,
30
+ maxRedirection: 5,
31
+ userAgent: undefined,
32
+ },
33
+ },
34
+ );
35
+ // With options
36
+ const maxRedirection = 10;
37
+ assert.deepEqual(
38
+ parse(webFingerCommand, [
39
+ ...argsWithResourcesOnly,
40
+ "-d",
41
+ "-u",
42
+ USER_AGENT,
43
+ "--max-redirection",
44
+ String(maxRedirection),
45
+ "--allow-private-address",
46
+ ]),
47
+ {
48
+ success: true,
49
+ value: {
50
+ debug: true,
51
+ command: COMMAND,
52
+ resources: RESOURCES,
53
+ allowPrivateAddresses: true,
54
+ maxRedirection,
55
+ userAgent: USER_AGENT,
56
+ },
57
+ },
58
+ );
59
+ // Wrong option
60
+ const wrongOptionResult = parse(webFingerCommand, [
61
+ ...argsWithResourcesOnly,
62
+ "-Q",
63
+ ]);
64
+ assert.ok(!wrongOptionResult.success);
65
+ // Wrong option value
66
+ const wrongOptionValueResult = parse(
67
+ webFingerCommand,
68
+ [...argsWithResourcesOnly, "--max-redirection", "-10"],
69
+ );
70
+ assert.ok(!wrongOptionValueResult.success);
71
+ });
72
+
73
+ test("Test lookupSingleWebFinger", async () => {
74
+ const aliases = (await Array.fromAsync(
75
+ RESOURCES,
76
+ (resource) => lookupSingleWebFinger({ resource }),
77
+ )).map((w) => w?.aliases?.[0]);
78
+ assert.deepEqual(aliases, ALIASES);
79
+ });
@@ -0,0 +1,2 @@
1
+ export { default as runWebFinger } from "./action.ts";
2
+ export { webFingerCommand } from "./command.ts";
@@ -0,0 +1,24 @@
1
+ import { defineConfig } from "tsdown";
2
+
3
+ export default defineConfig({
4
+ entry: ["src/mod.ts", "src/kv.bun.ts", "src/kv.node.ts"],
5
+ platform: "node",
6
+ unbundle: true,
7
+ inputOptions: {
8
+ onwarn(warning, defaultHandler) {
9
+ if (
10
+ warning.code === "UNRESOLVED_IMPORT" &&
11
+ ["#kv", "bun:sqlite"].includes(warning.exporter ?? "")
12
+ ) {
13
+ return;
14
+ }
15
+ defaultHandler(warning);
16
+ },
17
+ },
18
+ outputOptions(outputOptions) {
19
+ outputOptions.intro = `
20
+ import { Temporal } from "@js-temporal/polyfill";
21
+ `;
22
+ return outputOptions;
23
+ },
24
+ });