@fedify/cli 2.0.0-pr.479.1922 → 2.0.0

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 (160) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +3 -3
  3. package/dist/cache.js +17 -3
  4. package/dist/config.js +105 -0
  5. package/dist/deno.js +18 -8
  6. package/dist/generate-vocab/action.js +1 -1
  7. package/dist/imagerenderer.js +1 -1
  8. package/dist/inbox/rendercode.js +11 -21
  9. package/dist/inbox.js +162 -132
  10. package/dist/init/mod.js +3 -3
  11. package/dist/log.js +35 -1
  12. package/dist/lookup.js +55 -23
  13. package/dist/mod.js +95 -18
  14. package/dist/nodeinfo.js +39 -22
  15. package/dist/options.js +84 -0
  16. package/dist/relay.js +136 -0
  17. package/dist/tempserver.js +15 -8
  18. package/dist/tunnel.js +6 -10
  19. package/dist/utils.js +19 -108
  20. package/dist/webfinger/action.js +1 -1
  21. package/dist/webfinger/command.js +17 -9
  22. package/dist/webfinger/lib.js +3 -3
  23. package/package.json +50 -28
  24. package/deno.json +0 -71
  25. package/dist/globals.js +0 -49
  26. package/dist/init/action/configs.js +0 -91
  27. package/dist/init/action/const.js +0 -10
  28. package/dist/init/action/deps.js +0 -50
  29. package/dist/init/action/dir.js +0 -16
  30. package/dist/init/action/env.js +0 -13
  31. package/dist/init/action/install.js +0 -20
  32. package/dist/init/action/mod.js +0 -39
  33. package/dist/init/action/notice.js +0 -55
  34. package/dist/init/action/patch.js +0 -147
  35. package/dist/init/action/precommand.js +0 -28
  36. package/dist/init/action/recommend.js +0 -24
  37. package/dist/init/action/set.js +0 -31
  38. package/dist/init/action/templates.js +0 -58
  39. package/dist/init/action/utils.js +0 -50
  40. package/dist/init/ask/dir.js +0 -82
  41. package/dist/init/ask/kv.js +0 -44
  42. package/dist/init/ask/mod.js +0 -16
  43. package/dist/init/ask/mq.js +0 -46
  44. package/dist/init/ask/pm.js +0 -49
  45. package/dist/init/ask/wf.js +0 -29
  46. package/dist/init/command.js +0 -50
  47. package/dist/init/const.js +0 -31
  48. package/dist/init/json/biome.js +0 -24
  49. package/dist/init/json/kv.js +0 -53
  50. package/dist/init/json/mq.js +0 -72
  51. package/dist/init/json/pm.js +0 -44
  52. package/dist/init/json/rt.js +0 -39
  53. package/dist/init/json/vscode-settings-for-deno.js +0 -53
  54. package/dist/init/json/vscode-settings.js +0 -49
  55. package/dist/init/lib.js +0 -136
  56. package/dist/init/templates/defaults/federation.ts.tpl +0 -23
  57. package/dist/init/templates/defaults/logging.ts.tpl +0 -23
  58. package/dist/init/templates/express/app.ts.tpl +0 -16
  59. package/dist/init/templates/express/index.ts.tpl +0 -6
  60. package/dist/init/templates/hono/app.tsx.tpl +0 -14
  61. package/dist/init/templates/hono/index/bun.ts.tpl +0 -10
  62. package/dist/init/templates/hono/index/deno.ts.tpl +0 -13
  63. package/dist/init/templates/hono/index/node.ts.tpl +0 -14
  64. package/dist/init/templates/next/middleware.ts.tpl +0 -45
  65. package/dist/init/templates/nitro/.env.test.tpl +0 -1
  66. package/dist/init/templates/nitro/nitro.config.ts.tpl +0 -14
  67. package/dist/init/templates/nitro/server/error.ts.tpl +0 -3
  68. package/dist/init/templates/nitro/server/middleware/federation.ts.tpl +0 -8
  69. package/dist/init/test/action.js +0 -17
  70. package/dist/init/test/create.js +0 -100
  71. package/dist/init/test/fill.js +0 -32
  72. package/dist/init/test/lookup.js +0 -190
  73. package/dist/init/test/run.js +0 -25
  74. package/dist/init/test/utils.js +0 -17
  75. package/dist/init/webframeworks.js +0 -136
  76. package/scripts/pack.ts +0 -71
  77. package/src/cache.ts +0 -17
  78. package/src/docloader.ts +0 -67
  79. package/src/generate-vocab/action.ts +0 -17
  80. package/src/generate-vocab/command.ts +0 -44
  81. package/src/generate-vocab/mod.ts +0 -2
  82. package/src/globals.ts +0 -43
  83. package/src/imagerenderer.ts +0 -149
  84. package/src/inbox/entry.ts +0 -10
  85. package/src/inbox/rendercode.ts +0 -68
  86. package/src/inbox/view.tsx +0 -598
  87. package/src/inbox.tsx +0 -536
  88. package/src/init/action/configs.ts +0 -133
  89. package/src/init/action/const.ts +0 -9
  90. package/src/init/action/deps.ts +0 -161
  91. package/src/init/action/dir.ts +0 -11
  92. package/src/init/action/env.ts +0 -14
  93. package/src/init/action/install.ts +0 -24
  94. package/src/init/action/mod.ts +0 -66
  95. package/src/init/action/notice.ts +0 -103
  96. package/src/init/action/patch.ts +0 -233
  97. package/src/init/action/precommand.ts +0 -29
  98. package/src/init/action/recommend.ts +0 -38
  99. package/src/init/action/set.ts +0 -65
  100. package/src/init/action/templates.ts +0 -96
  101. package/src/init/action/utils.ts +0 -64
  102. package/src/init/ask/dir.ts +0 -98
  103. package/src/init/ask/kv.ts +0 -82
  104. package/src/init/ask/mod.ts +0 -23
  105. package/src/init/ask/mq.ts +0 -86
  106. package/src/init/ask/pm.ts +0 -58
  107. package/src/init/ask/wf.ts +0 -27
  108. package/src/init/command.ts +0 -135
  109. package/src/init/const.ts +0 -4
  110. package/src/init/json/biome.json +0 -17
  111. package/src/init/json/kv.json +0 -39
  112. package/src/init/json/mq.json +0 -95
  113. package/src/init/json/pm.json +0 -47
  114. package/src/init/json/rt.json +0 -42
  115. package/src/init/json/vscode-settings-for-deno.json +0 -43
  116. package/src/init/json/vscode-settings.json +0 -41
  117. package/src/init/lib.ts +0 -223
  118. package/src/init/mod.ts +0 -3
  119. package/src/init/templates/defaults/federation.ts.tpl +0 -23
  120. package/src/init/templates/defaults/logging.ts.tpl +0 -23
  121. package/src/init/templates/express/app.ts.tpl +0 -16
  122. package/src/init/templates/express/index.ts.tpl +0 -6
  123. package/src/init/templates/hono/app.tsx.tpl +0 -14
  124. package/src/init/templates/hono/index/bun.ts.tpl +0 -10
  125. package/src/init/templates/hono/index/deno.ts.tpl +0 -13
  126. package/src/init/templates/hono/index/node.ts.tpl +0 -14
  127. package/src/init/templates/next/middleware.ts.tpl +0 -45
  128. package/src/init/templates/nitro/.env.test.tpl +0 -1
  129. package/src/init/templates/nitro/nitro.config.ts.tpl +0 -14
  130. package/src/init/templates/nitro/server/error.ts.tpl +0 -3
  131. package/src/init/templates/nitro/server/middleware/federation.ts.tpl +0 -8
  132. package/src/init/test/action.ts +0 -28
  133. package/src/init/test/create.ts +0 -137
  134. package/src/init/test/fill.ts +0 -67
  135. package/src/init/test/lookup.ts +0 -254
  136. package/src/init/test/run.ts +0 -39
  137. package/src/init/test/types.ts +0 -27
  138. package/src/init/test/utils.ts +0 -21
  139. package/src/init/types.ts +0 -89
  140. package/src/init/webframeworks.ts +0 -168
  141. package/src/kv.bun.ts +0 -12
  142. package/src/kv.node.ts +0 -11
  143. package/src/log.ts +0 -64
  144. package/src/lookup.test.ts +0 -182
  145. package/src/lookup.ts +0 -563
  146. package/src/mod.ts +0 -62
  147. package/src/nodeinfo.test.ts +0 -229
  148. package/src/nodeinfo.ts +0 -454
  149. package/src/table.ts +0 -17
  150. package/src/tempserver.ts +0 -87
  151. package/src/tunnel.test.ts +0 -157
  152. package/src/tunnel.ts +0 -94
  153. package/src/utils.ts +0 -254
  154. package/src/webfinger/action.ts +0 -50
  155. package/src/webfinger/command.ts +0 -64
  156. package/src/webfinger/error.ts +0 -47
  157. package/src/webfinger/lib.ts +0 -37
  158. package/src/webfinger/mod.test.ts +0 -79
  159. package/src/webfinger/mod.ts +0 -2
  160. package/tsdown.config.ts +0 -35
package/src/tempserver.ts DELETED
@@ -1,87 +0,0 @@
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
- }
@@ -1,157 +0,0 @@
1
- import type { Tunnel, TunnelOptions } from "@hongminhee/localtunnel";
2
- import { run } from "@optique/run";
3
- import { deepEqual, rejects } from "node:assert/strict";
4
- import test from "node:test";
5
- import type { Ora } from "ora";
6
- import { runTunnel, tunnelCommand } from "./tunnel.ts";
7
-
8
- test("tunnel command structure", () => {
9
- const testCommandWithOptions = run(tunnelCommand, {
10
- args: ["tunnel", "3001", "-s", "pinggy.io", "-d"],
11
- });
12
- const testCommandWithoutOptions = run(tunnelCommand, {
13
- args: ["tunnel", "3000"],
14
- });
15
-
16
- deepEqual(testCommandWithOptions.command, "tunnel");
17
- deepEqual(testCommandWithOptions.port, 3001);
18
- deepEqual(testCommandWithOptions.service, "pinggy.io");
19
- deepEqual(testCommandWithOptions.debug, true);
20
-
21
- deepEqual(testCommandWithoutOptions.port, 3000);
22
- deepEqual(testCommandWithoutOptions.service, undefined);
23
- deepEqual(testCommandWithoutOptions.debug, false);
24
- });
25
-
26
- test("tunnel successfully creates and manages tunnel", async () => {
27
- const mockCommand = {
28
- command: "tunnel" as const,
29
- port: 3001,
30
- service: "pinggy.io" as const,
31
- debug: true,
32
- };
33
-
34
- const mockTunnel: Tunnel = {
35
- url: new URL("https://droar-218-152-125-59.a.free.pinggy.link/"),
36
- localPort: 3001,
37
- pid: 123,
38
- close: () => Promise.resolve(),
39
- };
40
-
41
- let openTunnelCalled = false;
42
- let openTunnelPort;
43
- let openTunnelService;
44
- let spinnerCalled = false;
45
- let openTunnelSucceed = false;
46
- let openTunnelFailed = false;
47
- let spinnerMsg;
48
-
49
- const mockDeps = {
50
- openTunnel: (args: TunnelOptions) => {
51
- openTunnelCalled = true;
52
- openTunnelPort = args.port;
53
- openTunnelService = args.service;
54
- return Promise.resolve(mockTunnel);
55
- },
56
- ora: () =>
57
- ({
58
- start() {
59
- spinnerCalled = true;
60
- return this;
61
- },
62
- fail(msg: string) {
63
- openTunnelFailed = true;
64
- spinnerMsg = msg;
65
- return this;
66
- },
67
- succeed(msg: string) {
68
- openTunnelSucceed = true;
69
- spinnerMsg = msg;
70
- return this;
71
- },
72
- }) as unknown as Ora,
73
- exit: (): never => {
74
- throw new Error();
75
- },
76
- };
77
-
78
- try {
79
- await runTunnel(mockCommand, mockDeps);
80
- } finally {
81
- deepEqual(openTunnelCalled, true);
82
- deepEqual(openTunnelPort, 3001);
83
- deepEqual(openTunnelService, "pinggy.io");
84
- deepEqual(openTunnelSucceed, true);
85
- deepEqual(openTunnelFailed, false);
86
- deepEqual(spinnerCalled, true);
87
- deepEqual(
88
- spinnerMsg,
89
- `Your local server at ${mockTunnel.localPort} is now publicly accessible:\n`,
90
- );
91
- }
92
- });
93
-
94
- test("tunnel fails to create a secure tunnel and handles error", async () => {
95
- const mockCommand = {
96
- command: "tunnel" as const,
97
- port: 3001,
98
- service: undefined,
99
- debug: false,
100
- };
101
-
102
- let openTunnelCalled = false;
103
- let openTunnelPort;
104
- let openTunnelService;
105
- let spinnerCalled = false;
106
- let openTunnelSucceed = false;
107
- let openTunnelFailed = false;
108
- let spinnerMsg;
109
-
110
- const mockDeps = {
111
- openTunnel: (args: TunnelOptions) => {
112
- openTunnelCalled = true;
113
- openTunnelPort = args.port;
114
- openTunnelService = args.service;
115
- return Promise.reject();
116
- },
117
- ora: () =>
118
- ({
119
- start() {
120
- spinnerCalled = true;
121
- return this;
122
- },
123
- fail(msg: string) {
124
- openTunnelFailed = true;
125
- spinnerMsg = msg;
126
- return this;
127
- },
128
- succeed(msg: string) {
129
- openTunnelSucceed = true;
130
- spinnerMsg = msg;
131
- return this;
132
- },
133
- }) as unknown as Ora,
134
- exit: (): never => {
135
- throw new Error("Process exit called");
136
- },
137
- };
138
-
139
- try {
140
- await rejects(
141
- () => runTunnel(mockCommand, mockDeps),
142
- Error,
143
- "Process exit called",
144
- );
145
- } finally {
146
- deepEqual(openTunnelCalled, true);
147
- deepEqual(openTunnelPort, 3001);
148
- deepEqual(openTunnelService, undefined);
149
- deepEqual(openTunnelSucceed, false);
150
- deepEqual(openTunnelFailed, true);
151
- deepEqual(spinnerCalled, true);
152
- deepEqual(
153
- spinnerMsg,
154
- "Failed to create a secure tunnel.",
155
- );
156
- }
157
- });
package/src/tunnel.ts DELETED
@@ -1,94 +0,0 @@
1
- import { openTunnel, type Tunnel } from "@hongminhee/localtunnel";
2
- import {
3
- argument,
4
- command,
5
- constant,
6
- type InferValue,
7
- integer,
8
- merge,
9
- message,
10
- object,
11
- option,
12
- optional,
13
- } from "@optique/core";
14
- import { choice } from "@optique/core/valueparser";
15
- import { print, printError } from "@optique/run";
16
- import process from "node:process";
17
- import ora from "ora";
18
- import { configureLogging, debugOption } from "./globals.ts";
19
-
20
- export const tunnelCommand = command(
21
- "tunnel",
22
- merge(
23
- "Tunnel options",
24
- object({
25
- command: constant("tunnel"),
26
- }),
27
- object({
28
- port: argument(integer({ metavar: "PORT", min: 0, max: 65535 }), {
29
- description: message`The local port number to expose.`,
30
- }),
31
- service: optional(
32
- option(
33
- "-s",
34
- "--service",
35
- choice(["localhost.run", "serveo.net", "pinggy.io"], {
36
- metavar: "SERVICE",
37
- }),
38
- { description: message`The tunneling service to use.` },
39
- ),
40
- ),
41
- }),
42
- debugOption,
43
- ),
44
- {
45
- brief:
46
- message`Expose a local HTTP server to the public internet using a secure tunnel.`,
47
- description:
48
- message`Expose a local HTTP server to the public internet using a secure tunnel.
49
-
50
- Note that the HTTP requests through the tunnel have X-Forwarded-* headers.`,
51
- },
52
- );
53
-
54
- export async function runTunnel(
55
- command: InferValue<typeof tunnelCommand>,
56
- deps: {
57
- openTunnel: typeof openTunnel;
58
- ora: typeof ora;
59
- exit: typeof process.exit;
60
- } = {
61
- openTunnel,
62
- ora,
63
- exit: process.exit,
64
- },
65
- ) {
66
- if (command.debug) {
67
- await configureLogging();
68
- }
69
- const spinner = deps.ora({
70
- text: "Creating a secure tunnel...",
71
- discardStdin: false,
72
- }).start();
73
- let tunnel: Tunnel;
74
- try {
75
- tunnel = await deps.openTunnel({
76
- port: command.port,
77
- service: command.service,
78
- });
79
- } catch (error) {
80
- if (command.debug) {
81
- printError(message`${String(error)}`);
82
- }
83
- spinner.fail("Failed to create a secure tunnel.");
84
- deps.exit(1);
85
- }
86
- spinner.succeed(
87
- `Your local server at ${command.port} is now publicly accessible:\n`,
88
- );
89
- print(message`${tunnel.url.href}`);
90
- print(message`\nPress ^C to close the tunnel.`);
91
- process.on("SIGINT", async () => {
92
- await tunnel.close();
93
- });
94
- }
package/src/utils.ts DELETED
@@ -1,254 +0,0 @@
1
- import { isObject } from "@fxts/core";
2
- import { message } from "@optique/core";
3
- import { print, printError } from "@optique/run";
4
- import { Chalk } from "chalk";
5
- import { highlight } from "cli-highlight";
6
- import { flow, toMerged } from "es-toolkit";
7
- import { spawn } from "node:child_process";
8
- import { writeFile } from "node:fs/promises";
9
- import process from "node:process";
10
- import util from "node:util";
11
-
12
- export const colorEnabled: boolean = process.stdout.isTTY &&
13
- !("NO_COLOR" in process.env && process.env.NO_COLOR !== "");
14
-
15
- export const colors = new Chalk(colorEnabled ? {} : { level: 0 });
16
-
17
- export function formatObject(
18
- obj: unknown,
19
- colors?: boolean,
20
- json?: boolean,
21
- ): string {
22
- const enableColors = colors ?? colorEnabled;
23
- if (!json) return util.inspect(obj, { colors: enableColors });
24
- const formatted = JSON.stringify(obj, null, 2);
25
- if (enableColors) {
26
- return highlight(formatted, { language: "json" });
27
- }
28
- return formatted;
29
- }
30
-
31
- export const isPromise = <T>(a: unknown): a is Promise<T> =>
32
- a instanceof Promise;
33
-
34
- export function set<K extends PropertyKey, T extends object, S>(
35
- key: K,
36
- f: (value: T) => S,
37
- ): (
38
- obj: T,
39
- ) => S extends Promise<infer U> ? Promise<T & { [P in K]: Awaited<U> }>
40
- : T & { [P in K]: S } {
41
- return ((obj) => {
42
- const result = f(obj);
43
- if (isPromise<S extends Promise<infer U> ? U : never>(result)) {
44
- return result.then((value) => ({ ...obj, [key]: value })) as S extends
45
- Promise<infer U> ? Promise<
46
- T & { [P in K]: Awaited<U> }
47
- >
48
- : never;
49
- }
50
- return ({ ...obj, [key]: result }) as S extends Promise<infer _> ? never
51
- : T & { [P in K]: S };
52
- });
53
- }
54
-
55
- export const merge =
56
- (source: Parameters<typeof toMerged>[1] = {}) =>
57
- (target: Parameters<typeof toMerged>[0] = {}) => toMerged(target, source);
58
-
59
- export const isNotFoundError = (e: unknown): e is { code: "ENOENT" } =>
60
- isObject(e) &&
61
- "code" in e &&
62
- e.code === "ENOENT";
63
-
64
- export class CommandError extends Error {
65
- public commandLine: string;
66
- constructor(
67
- message: string,
68
- public stdout: string,
69
- public stderr: string,
70
- public code: number,
71
- public command: string[],
72
- ) {
73
- super(message);
74
- this.name = "CommandError";
75
- this.commandLine = command.join(" ");
76
- }
77
- }
78
-
79
- export const runSubCommand = async <Opt extends Parameters<typeof spawn>[2]>(
80
- command: string[],
81
- options: Opt,
82
- ): Promise<{
83
- stdout: string;
84
- stderr: string;
85
- }> => {
86
- const commands = // split by "&&"
87
- command.reduce<string[][]>((acc, cur) => {
88
- if (cur === "&&") {
89
- acc.push([]);
90
- } else {
91
- if (acc.length === 0) acc.push([]);
92
- acc[acc.length - 1].push(cur);
93
- }
94
- return acc;
95
- }, []);
96
-
97
- const results = { stdout: "", stderr: "" };
98
-
99
- for (const cmd of commands) {
100
- try {
101
- const result = await runSingularCommand(cmd, options);
102
- results.stdout += (results.stdout ? "\n" : "") + result.stdout;
103
- results.stderr += (results.stderr ? "\n" : "") + result.stderr;
104
- } catch (e) {
105
- if (e instanceof CommandError) {
106
- results.stdout += (results.stdout ? "\n" : "") + e.stdout;
107
- results.stderr += (results.stderr ? "\n" : "") + e.stderr;
108
- }
109
- throw e;
110
- }
111
- }
112
- return results;
113
- };
114
-
115
- const runSingularCommand = (
116
- command: string[],
117
- options: Parameters<typeof spawn>[2],
118
- ) =>
119
- new Promise<{
120
- stdout: string;
121
- stderr: string;
122
- }>((resolve, reject) => {
123
- let stdout = "";
124
- let stderr = "";
125
- const child = spawn(command[0], command.slice(1), options);
126
-
127
- child.stdout?.on("data", (data) => {
128
- stdout += data.toString();
129
- });
130
- child.stderr?.on("data", (data) => {
131
- stderr += data.toString();
132
- });
133
-
134
- child.on("close", (code) => {
135
- if (code === 0) {
136
- resolve({
137
- stdout: stdout.trim(),
138
- stderr: stderr.trim(),
139
- });
140
- } else {
141
- reject(
142
- new CommandError(
143
- `Command exited with code ${code ?? "unknown"}`,
144
- stdout.trim(),
145
- stderr.trim(),
146
- code ?? -1,
147
- command,
148
- ),
149
- );
150
- }
151
- });
152
-
153
- child.on("error", (error) => {
154
- reject(error);
155
- });
156
- });
157
-
158
- export type RequiredNotNull<T> = {
159
- [P in keyof T]: NonNullable<T[P]>;
160
- };
161
-
162
- export const getCwd = () => process.cwd();
163
-
164
- export const replace = (
165
- pattern: string | RegExp,
166
- replacement: string | ((substring: string, ...args: unknown[]) => string),
167
- ) =>
168
- (text: string): string => text.replace(pattern, replacement as string);
169
-
170
- export const replaceAll = (
171
- pattern: string | RegExp,
172
- replacement: string | ((substring: string, ...args: unknown[]) => string),
173
- ) =>
174
- (text: string): string => text.replaceAll(pattern, replacement as string);
175
-
176
- export const getOsType = () => process.platform;
177
-
178
- export async function writeTextFile(
179
- path: string,
180
- content: string,
181
- ): Promise<void> {
182
- const encoder = new TextEncoder();
183
- const data = encoder.encode(content);
184
- return await writeFile(path, data);
185
- }
186
-
187
- export const resolveProps = async <T extends object>(obj: T): Promise<
188
- { [P in keyof T]: Awaited<T[P]> }
189
- > =>
190
- Object.fromEntries(
191
- await Array.fromAsync(
192
- Object.entries(obj),
193
- async ([k, v]) => [k, await v],
194
- ),
195
- ) as Promise<{ [P in keyof T]: Awaited<T[P]> }>;
196
-
197
- export const formatJson = (obj: unknown) => JSON.stringify(obj, null, 2) + "\n";
198
-
199
- export const notEmpty = <T extends string | { length: number }>(s: T) =>
200
- s.length > 0;
201
-
202
- export const notEmptyObj = <T extends Record<PropertyKey, never> | object>(
203
- obj: T,
204
- ): obj is Exclude<T, Record<PropertyKey, never>> => Object.keys(obj).length > 0;
205
-
206
- export const exit = (code: number) => process.exit(code);
207
-
208
- /**
209
- * Generic type to represent the types of the items in iterables.
210
- */
211
- export type ItersItems<T extends Iterable<unknown>[]> = T extends [] ? []
212
- : T extends [infer Head, ...infer Tail]
213
- ? Head extends Iterable<infer Item>
214
- ? Tail extends Iterable<unknown>[] ? [Item, ...ItersItems<Tail>]
215
- : [Item]
216
- : never
217
- : never;
218
-
219
- /**
220
- * ```haskell
221
- * product::[[a], [b], ...] -> [[a, b, ...]]
222
- * ```
223
- *
224
- * Cartesian product of the input iterables.
225
- * Inspired by Python's `itertools.product`.
226
- *
227
- * @param {...Iterable<unknown>} iters - The input iterables to compute the Cartesian product.
228
- * @returns {Generator<ItersItems<T>>} A generator that yields arrays containing one element from each iterable.
229
- *
230
- * @example
231
- * ```ts
232
- * const iter1 = [1, 2];
233
- * const iter2 = ['a', 'b'];
234
- * const iter3 = [true, false];
235
- * const productIter = product(iter1, iter2, iter3);
236
- * console.log(Array.from(productIter)); // Output: [[1, 'a', true], [1, 'a', false], [
237
- */
238
- export function* product<T extends Iterable<unknown>[]>(
239
- ...[head, ...tail]: T
240
- ): Generator<ItersItems<T>> {
241
- if (!head) yield [] as ItersItems<T>;
242
- else {
243
- for (const x of head) {
244
- for (const xs of product(...tail)) yield [x, ...xs] as ItersItems<T>;
245
- }
246
- }
247
- }
248
-
249
- export type GeneratedType<T extends Generator> = T extends
250
- Generator<unknown, infer R, unknown> ? R : never;
251
-
252
- type PrintMessage = (...args: Parameters<typeof message>) => void;
253
- export const printMessage: PrintMessage = flow(message, print);
254
- export const printErrorMessage: PrintMessage = flow(message, printError);
@@ -1,50 +0,0 @@
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
- }
@@ -1,64 +0,0 @@
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(
45
- argument(string({ metavar: "RESOURCE" }), {
46
- description: message`WebFinger resource(s) to look up.`,
47
- }),
48
- { min: 1 },
49
- ),
50
- userAgent,
51
- allowPrivateAddresses,
52
- maxRedirection,
53
- }),
54
- debugOption,
55
- ),
56
- {
57
- brief: message`Look up WebFinger resources.`,
58
- description: message`Look up WebFinger resources.
59
-
60
- The argument can be multiple.`,
61
- },
62
- );
63
-
64
- export type WebFingerCommand = InferValue<typeof webFingerCommand>;