@fedify/cli 2.0.7 → 2.0.9
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.
- package/dist/cache.js +7 -16
- package/dist/config.js +5 -13
- package/dist/deno.js +3 -79
- package/dist/docloader.js +3 -8
- package/dist/generate-vocab/action.js +4 -8
- package/dist/generate-vocab/command.js +2 -7
- package/dist/generate-vocab/mod.js +4 -5
- package/dist/imagerenderer.js +10 -20
- package/dist/inbox/rendercode.js +3 -8
- package/dist/inbox/view.js +7 -13
- package/dist/inbox.js +25 -29
- package/dist/init/mod.js +2 -5
- package/dist/kv.bun.js +4 -10
- package/dist/kv.node.js +4 -10
- package/dist/log.js +5 -9
- package/dist/lookup.js +45 -54
- package/dist/mod.js +16 -30
- package/dist/nodeinfo.js +35 -45
- package/dist/options.js +9 -26
- package/dist/relay.js +20 -25
- package/dist/table.js +2 -5
- package/dist/tempserver.js +13 -20
- package/dist/tunnel.js +9 -13
- package/dist/utils.js +7 -13
- package/dist/webfinger/action.js +5 -11
- package/dist/webfinger/command.js +2 -6
- package/dist/webfinger/error.js +2 -6
- package/dist/webfinger/lib.js +3 -8
- package/dist/webfinger/mod.js +4 -5
- package/package.json +11 -11
package/dist/lookup.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { Temporal } from "@js-temporal/polyfill";
|
|
3
|
-
|
|
1
|
+
import "@js-temporal/polyfill";
|
|
4
2
|
import { configContext } from "./config.js";
|
|
5
3
|
import { getContextLoader, getDocumentLoader } from "./docloader.js";
|
|
6
4
|
import { configureLogging } from "./log.js";
|
|
@@ -8,16 +6,15 @@ import { createTunnelServiceOption, userAgentOption } from "./options.js";
|
|
|
8
6
|
import { spawnTemporaryServer } from "./tempserver.js";
|
|
9
7
|
import { colorEnabled, colors, formatObject } from "./utils.js";
|
|
10
8
|
import { renderImages } from "./imagerenderer.js";
|
|
11
|
-
import process from "node:process";
|
|
12
9
|
import { argument, choice, command, constant, flag, float, map, merge, message, multiple, object, option, optionNames, optional, or, string, withDefault } from "@optique/core";
|
|
13
10
|
import { path, print, printError } from "@optique/run";
|
|
14
11
|
import { createWriteStream } from "node:fs";
|
|
12
|
+
import process from "node:process";
|
|
15
13
|
import { bindConfig } from "@optique/config";
|
|
16
14
|
import { generateCryptoKeyPair, getAuthenticatedDocumentLoader, respondWithObject } from "@fedify/fedify";
|
|
17
15
|
import { Application, Collection, CryptographicKey, Object as Object$1, lookupObject, traverseCollection } from "@fedify/vocab";
|
|
18
16
|
import { getLogger } from "@logtape/logtape";
|
|
19
17
|
import ora from "ora";
|
|
20
|
-
|
|
21
18
|
//#region src/lookup.ts
|
|
22
19
|
const logger = getLogger([
|
|
23
20
|
"fedify",
|
|
@@ -82,8 +79,8 @@ The arguments can be either URLs or actor handles (e.g., ${"@username@domain"}),
|
|
|
82
79
|
});
|
|
83
80
|
var TimeoutError = class extends Error {
|
|
84
81
|
name = "TimeoutError";
|
|
85
|
-
constructor(message
|
|
86
|
-
super(message
|
|
82
|
+
constructor(message) {
|
|
83
|
+
super(message);
|
|
87
84
|
this.name = "TimeoutError";
|
|
88
85
|
}
|
|
89
86
|
};
|
|
@@ -95,34 +92,32 @@ async function findAllImages(obj) {
|
|
|
95
92
|
if (image && image.url instanceof URL) result.push(image.url);
|
|
96
93
|
return result;
|
|
97
94
|
}
|
|
98
|
-
async function writeObjectToStream(object
|
|
95
|
+
async function writeObjectToStream(object, outputPath, format, contextLoader) {
|
|
99
96
|
const stream = outputPath ? createWriteStream(outputPath) : process.stdout;
|
|
100
97
|
let content;
|
|
101
98
|
let json = true;
|
|
102
99
|
let imageUrls = [];
|
|
103
|
-
if (format) if (format === "raw") content = await object
|
|
104
|
-
else if (format === "compact") content = await object
|
|
100
|
+
if (format) if (format === "raw") content = await object.toJsonLd({ contextLoader });
|
|
101
|
+
else if (format === "compact") content = await object.toJsonLd({
|
|
105
102
|
format: "compact",
|
|
106
103
|
contextLoader
|
|
107
104
|
});
|
|
108
|
-
else if (format === "expand") content = await object
|
|
105
|
+
else if (format === "expand") content = await object.toJsonLd({
|
|
109
106
|
format: "expand",
|
|
110
107
|
contextLoader
|
|
111
108
|
});
|
|
112
109
|
else {
|
|
113
|
-
content = object
|
|
110
|
+
content = object;
|
|
114
111
|
json = false;
|
|
115
112
|
}
|
|
116
113
|
else {
|
|
117
|
-
content = object
|
|
114
|
+
content = object;
|
|
118
115
|
json = false;
|
|
119
116
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const encoder = new TextEncoder();
|
|
123
|
-
const bytes = encoder.encode(content + "\n");
|
|
117
|
+
content = formatObject(content, colorEnabled && outputPath === void 0, json);
|
|
118
|
+
const bytes = new TextEncoder().encode(content + "\n");
|
|
124
119
|
stream.write(bytes);
|
|
125
|
-
if (object
|
|
120
|
+
if (object instanceof Object$1) imageUrls = await findAllImages(object);
|
|
126
121
|
if (!outputPath && imageUrls.length > 0) await renderImages(imageUrls);
|
|
127
122
|
}
|
|
128
123
|
const signalTimers = /* @__PURE__ */ new WeakMap();
|
|
@@ -158,23 +153,21 @@ function handleTimeoutError(spinner, timeoutSeconds, url) {
|
|
|
158
153
|
spinner.fail(`Request timed out after ${timeoutSeconds} seconds${urlText}.`);
|
|
159
154
|
printError(message`Try increasing the timeout with -T/--timeout option or check network connectivity.`);
|
|
160
155
|
}
|
|
161
|
-
async function runLookup(command
|
|
162
|
-
if (command
|
|
156
|
+
async function runLookup(command) {
|
|
157
|
+
if (command.urls.length < 1) {
|
|
163
158
|
printError(message`At least one URL or actor handle must be provided.`);
|
|
164
159
|
process.exit(1);
|
|
165
160
|
}
|
|
166
|
-
if (command
|
|
161
|
+
if (command.debug) await configureLogging();
|
|
167
162
|
const spinner = ora({
|
|
168
|
-
text: `Looking up the ${command
|
|
163
|
+
text: `Looking up the ${command.traverse ? "collection" : command.urls.length > 1 ? "objects" : "object"}...`,
|
|
169
164
|
discardStdin: false
|
|
170
165
|
}).start();
|
|
171
166
|
let server = void 0;
|
|
172
|
-
const
|
|
173
|
-
const
|
|
174
|
-
const baseContextLoader = await getContextLoader({ userAgent: command$1.userAgent });
|
|
175
|
-
const contextLoader = wrapDocumentLoaderWithTimeout(baseContextLoader, command$1.timeout);
|
|
167
|
+
const documentLoader = wrapDocumentLoaderWithTimeout(await getDocumentLoader({ userAgent: command.userAgent }), command.timeout);
|
|
168
|
+
const contextLoader = wrapDocumentLoaderWithTimeout(await getContextLoader({ userAgent: command.userAgent }), command.timeout);
|
|
176
169
|
let authLoader = void 0;
|
|
177
|
-
if (command
|
|
170
|
+
if (command.authorizedFetch) {
|
|
178
171
|
spinner.text = "Generating a one-time key pair...";
|
|
179
172
|
const key = await generateCryptoKeyPair();
|
|
180
173
|
spinner.text = "Spinning up a temporary ActivityPub server...";
|
|
@@ -204,33 +197,32 @@ async function runLookup(command$1) {
|
|
|
204
197
|
inbox: new URL("/inbox", serverUrl),
|
|
205
198
|
outbox: new URL("/outbox", serverUrl)
|
|
206
199
|
}), { contextLoader });
|
|
207
|
-
}, { service: command
|
|
208
|
-
|
|
200
|
+
}, { service: command.tunnelService });
|
|
201
|
+
authLoader = wrapDocumentLoaderWithTimeout(getAuthenticatedDocumentLoader({
|
|
209
202
|
keyId: new URL("#main-key", server.url),
|
|
210
203
|
privateKey: key.privateKey
|
|
211
204
|
}, { specDeterminer: {
|
|
212
205
|
determineSpec() {
|
|
213
|
-
return command
|
|
206
|
+
return command.firstKnock;
|
|
214
207
|
},
|
|
215
208
|
rememberSpec() {}
|
|
216
|
-
} });
|
|
217
|
-
authLoader = wrapDocumentLoaderWithTimeout(baseAuthLoader, command$1.timeout);
|
|
209
|
+
} }), command.timeout);
|
|
218
210
|
}
|
|
219
|
-
spinner.text = `Looking up the ${command
|
|
220
|
-
if (command
|
|
211
|
+
spinner.text = `Looking up the ${command.traverse ? "collection" : command.urls.length > 1 ? "objects" : "object"}...`;
|
|
212
|
+
if (command.traverse) {
|
|
221
213
|
let totalItems = 0;
|
|
222
|
-
for (let urlIndex = 0; urlIndex < command
|
|
223
|
-
const url = command
|
|
224
|
-
if (urlIndex > 0) spinner.text = `Looking up collection ${urlIndex + 1}/${command
|
|
214
|
+
for (let urlIndex = 0; urlIndex < command.urls.length; urlIndex++) {
|
|
215
|
+
const url = command.urls[urlIndex];
|
|
216
|
+
if (urlIndex > 0) spinner.text = `Looking up collection ${urlIndex + 1}/${command.urls.length}...`;
|
|
225
217
|
let collection;
|
|
226
218
|
try {
|
|
227
219
|
collection = await lookupObject(url, {
|
|
228
220
|
documentLoader: authLoader ?? documentLoader,
|
|
229
221
|
contextLoader,
|
|
230
|
-
userAgent: command
|
|
222
|
+
userAgent: command.userAgent
|
|
231
223
|
});
|
|
232
224
|
} catch (error) {
|
|
233
|
-
if (error instanceof TimeoutError) handleTimeoutError(spinner, command
|
|
225
|
+
if (error instanceof TimeoutError) handleTimeoutError(spinner, command.timeout, url);
|
|
234
226
|
else {
|
|
235
227
|
spinner.fail(`Failed to fetch object: ${colors.red(url)}.`);
|
|
236
228
|
if (authLoader == null) printError(message`It may be a private object. Try with -a/--authorized-fetch.`);
|
|
@@ -255,10 +247,10 @@ async function runLookup(command$1) {
|
|
|
255
247
|
for await (const item of traverseCollection(collection, {
|
|
256
248
|
documentLoader: authLoader ?? documentLoader,
|
|
257
249
|
contextLoader,
|
|
258
|
-
suppressError: command
|
|
250
|
+
suppressError: command.suppressErrors
|
|
259
251
|
})) {
|
|
260
|
-
if (!command
|
|
261
|
-
await writeObjectToStream(item, command
|
|
252
|
+
if (!command.output && (totalItems > 0 || collectionItems > 0)) print(message`${command.separator}`);
|
|
253
|
+
await writeObjectToStream(item, command.output, command.format, contextLoader);
|
|
262
254
|
collectionItems++;
|
|
263
255
|
totalItems++;
|
|
264
256
|
}
|
|
@@ -267,7 +259,7 @@ async function runLookup(command$1) {
|
|
|
267
259
|
url,
|
|
268
260
|
error
|
|
269
261
|
});
|
|
270
|
-
if (error instanceof TimeoutError) handleTimeoutError(spinner, command
|
|
262
|
+
if (error instanceof TimeoutError) handleTimeoutError(spinner, command.timeout, url);
|
|
271
263
|
else {
|
|
272
264
|
spinner.fail(`Failed to complete the traversal for: ${colors.red(url)}.`);
|
|
273
265
|
if (authLoader == null) printError(message`It may be a private object. Try with -a/--authorized-fetch.`);
|
|
@@ -282,12 +274,12 @@ async function runLookup(command$1) {
|
|
|
282
274
|
process.exit(0);
|
|
283
275
|
}
|
|
284
276
|
const promises = [];
|
|
285
|
-
for (const url of command
|
|
277
|
+
for (const url of command.urls) promises.push(lookupObject(url, {
|
|
286
278
|
documentLoader: authLoader ?? documentLoader,
|
|
287
279
|
contextLoader,
|
|
288
|
-
userAgent: command
|
|
280
|
+
userAgent: command.userAgent
|
|
289
281
|
}).catch((error) => {
|
|
290
|
-
if (error instanceof TimeoutError) handleTimeoutError(spinner, command
|
|
282
|
+
if (error instanceof TimeoutError) handleTimeoutError(spinner, command.timeout, url);
|
|
291
283
|
throw error;
|
|
292
284
|
}));
|
|
293
285
|
let objects;
|
|
@@ -301,8 +293,8 @@ async function runLookup(command$1) {
|
|
|
301
293
|
let success = true;
|
|
302
294
|
let i = 0;
|
|
303
295
|
for (const obj of objects) {
|
|
304
|
-
const url = command
|
|
305
|
-
if (i > 0) print(message`${command
|
|
296
|
+
const url = command.urls[i];
|
|
297
|
+
if (i > 0) print(message`${command.separator}`);
|
|
306
298
|
i++;
|
|
307
299
|
if (obj == null) {
|
|
308
300
|
spinner.fail(`Failed to fetch ${colors.red(url)}`);
|
|
@@ -310,15 +302,14 @@ async function runLookup(command$1) {
|
|
|
310
302
|
success = false;
|
|
311
303
|
} else {
|
|
312
304
|
spinner.succeed(`Fetched object: ${colors.green(url)}`);
|
|
313
|
-
await writeObjectToStream(obj, command
|
|
314
|
-
if (i < command
|
|
305
|
+
await writeObjectToStream(obj, command.output, command.format, contextLoader);
|
|
306
|
+
if (i < command.urls.length - 1) print(message`${command.separator}`);
|
|
315
307
|
}
|
|
316
308
|
}
|
|
317
|
-
if (success) spinner.succeed(command
|
|
309
|
+
if (success) spinner.succeed(command.urls.length > 1 ? "Successfully fetched all objects." : "Successfully fetched the object.");
|
|
318
310
|
await server?.close();
|
|
319
311
|
if (!success) process.exit(1);
|
|
320
|
-
if (success && command
|
|
312
|
+
if (success && command.output) spinner.succeed(`Successfully wrote output to ${colors.green(command.output)}.`);
|
|
321
313
|
}
|
|
322
|
-
|
|
323
314
|
//#endregion
|
|
324
|
-
export { lookupCommand, runLookup };
|
|
315
|
+
export { lookupCommand, runLookup };
|
package/dist/mod.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env -S node --disable-warning=ExperimentalWarning
|
|
2
|
-
|
|
3
|
-
import { Temporal } from "@js-temporal/polyfill";
|
|
4
|
-
|
|
2
|
+
import "@js-temporal/polyfill";
|
|
5
3
|
import { configContext, tryLoadToml } from "./config.js";
|
|
6
|
-
import
|
|
7
|
-
import
|
|
4
|
+
import runGenerateVocab from "./generate-vocab/action.js";
|
|
5
|
+
import generateVocabCommand from "./generate-vocab/command.js";
|
|
8
6
|
import "./generate-vocab/mod.js";
|
|
9
|
-
import
|
|
7
|
+
import { version } from "./deno.js";
|
|
10
8
|
import { globalOptions } from "./options.js";
|
|
11
9
|
import { inboxCommand, runInbox } from "./inbox.js";
|
|
12
10
|
import { initCommand, runInit } from "./init/mod.js";
|
|
@@ -14,19 +12,18 @@ import { nodeInfoCommand, runNodeInfo } from "./nodeinfo.js";
|
|
|
14
12
|
import { lookupCommand, runLookup } from "./lookup.js";
|
|
15
13
|
import { relayCommand, runRelay } from "./relay.js";
|
|
16
14
|
import { runTunnel, tunnelCommand } from "./tunnel.js";
|
|
17
|
-
import
|
|
15
|
+
import runWebFinger from "./webfinger/action.js";
|
|
18
16
|
import { webFingerCommand } from "./webfinger/command.js";
|
|
19
17
|
import "./webfinger/mod.js";
|
|
20
|
-
import { join } from "node:path";
|
|
21
|
-
import { homedir } from "node:os";
|
|
22
|
-
import process from "node:process";
|
|
23
18
|
import { runWithConfig } from "@optique/config/run";
|
|
24
19
|
import { group, merge, message, or } from "@optique/core";
|
|
25
20
|
import { printError } from "@optique/run";
|
|
26
21
|
import { merge as merge$1 } from "es-toolkit";
|
|
27
22
|
import { readFileSync } from "node:fs";
|
|
23
|
+
import { homedir } from "node:os";
|
|
24
|
+
import { join } from "node:path";
|
|
25
|
+
import process from "node:process";
|
|
28
26
|
import { parse } from "smol-toml";
|
|
29
|
-
|
|
30
27
|
//#region src/mod.ts
|
|
31
28
|
/**
|
|
32
29
|
* Returns the system-wide configuration file paths.
|
|
@@ -34,10 +31,7 @@ import { parse } from "smol-toml";
|
|
|
34
31
|
* - Windows: Uses `%ProgramData%` (default: C:\ProgramData)
|
|
35
32
|
*/
|
|
36
33
|
function getSystemConfigPaths() {
|
|
37
|
-
if (process.platform === "win32")
|
|
38
|
-
const programData = process.env.ProgramData || "C:\\ProgramData";
|
|
39
|
-
return [join(programData, "fedify", "config.toml")];
|
|
40
|
-
}
|
|
34
|
+
if (process.platform === "win32") return [join(process.env.ProgramData || "C:\\ProgramData", "fedify", "config.toml")];
|
|
41
35
|
return (process.env.XDG_CONFIG_DIRS || "/etc/xdg").split(":").map((dir) => join(dir, "fedify", "config.toml"));
|
|
42
36
|
}
|
|
43
37
|
/**
|
|
@@ -46,21 +40,16 @@ function getSystemConfigPaths() {
|
|
|
46
40
|
* - Windows: `%APPDATA%\fedify\config.toml`
|
|
47
41
|
*/
|
|
48
42
|
function getUserConfigPath() {
|
|
49
|
-
if (process.platform === "win32")
|
|
50
|
-
|
|
51
|
-
return join(appData, "fedify", "config.toml");
|
|
52
|
-
}
|
|
53
|
-
const xdgConfigHome = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
|
|
54
|
-
return join(xdgConfigHome, "fedify", "config.toml");
|
|
43
|
+
if (process.platform === "win32") return join(process.env.APPDATA || join(homedir(), "AppData", "Roaming"), "fedify", "config.toml");
|
|
44
|
+
return join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "fedify", "config.toml");
|
|
55
45
|
}
|
|
56
|
-
const command$1 = merge(or(group("Generating code", or(initCommand,
|
|
46
|
+
const command$1 = merge(or(group("Generating code", or(initCommand, generateVocabCommand)), group("ActivityPub tools", or(webFingerCommand, lookupCommand, inboxCommand, nodeInfoCommand, relayCommand)), group("Network tools", tunnelCommand)), globalOptions);
|
|
57
47
|
async function main() {
|
|
58
48
|
const result = await runWithConfig(command$1, configContext, {
|
|
59
49
|
programName: "fedify",
|
|
60
50
|
load: (parsed) => {
|
|
61
51
|
if (parsed.ignoreConfig) return {};
|
|
62
|
-
const
|
|
63
|
-
const system = systemConfigs.reduce((acc, config) => merge$1(acc, config), {});
|
|
52
|
+
const system = getSystemConfigPaths().map(tryLoadToml).reduce((acc, config) => merge$1(acc, config), {});
|
|
64
53
|
const user = tryLoadToml(getUserConfigPath());
|
|
65
54
|
const project = tryLoadToml(join(process.cwd(), ".fedify.toml"));
|
|
66
55
|
let custom = {};
|
|
@@ -85,7 +74,7 @@ async function main() {
|
|
|
85
74
|
},
|
|
86
75
|
version: {
|
|
87
76
|
mode: "both",
|
|
88
|
-
value:
|
|
77
|
+
value: version,
|
|
89
78
|
group: "Meta commands"
|
|
90
79
|
},
|
|
91
80
|
completion: {
|
|
@@ -108,10 +97,7 @@ async function main() {
|
|
|
108
97
|
else if (result.command === "tunnel") await runTunnel(result);
|
|
109
98
|
else if (result.command === "generate-vocab") await runGenerateVocab(result);
|
|
110
99
|
else if (result.command === "relay") await runRelay(result);
|
|
111
|
-
else {
|
|
112
|
-
const _exhaustiveCheck = result;
|
|
113
|
-
}
|
|
114
100
|
}
|
|
115
101
|
await main();
|
|
116
|
-
|
|
117
|
-
|
|
102
|
+
//#endregion
|
|
103
|
+
export {};
|
package/dist/nodeinfo.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
import { Temporal } from "@js-temporal/polyfill";
|
|
3
|
-
|
|
1
|
+
import "@js-temporal/polyfill";
|
|
4
2
|
import { configContext } from "./config.js";
|
|
5
3
|
import { userAgentOption } from "./options.js";
|
|
6
4
|
import { colors, formatObject } from "./utils.js";
|
|
7
|
-
import os from "node:os";
|
|
8
|
-
import process from "node:process";
|
|
9
5
|
import { argument, command, constant, flag, group, merge, message, object, string, text } from "@optique/core";
|
|
10
6
|
import { print, printError } from "@optique/run";
|
|
7
|
+
import os from "node:os";
|
|
8
|
+
import process from "node:process";
|
|
11
9
|
import { bindConfig } from "@optique/config";
|
|
12
10
|
import { getNodeInfo } from "@fedify/fedify";
|
|
13
11
|
import { getLogger } from "@logtape/logtape";
|
|
@@ -17,7 +15,6 @@ import { createJimp } from "@jimp/core";
|
|
|
17
15
|
import webp from "@jimp/wasm-webp";
|
|
18
16
|
import { isICO, parseICO } from "icojs";
|
|
19
17
|
import { defaultFormats, defaultPlugins, intToRGBA } from "jimp";
|
|
20
|
-
|
|
21
18
|
//#region src/nodeinfo.ts
|
|
22
19
|
const logger = getLogger([
|
|
23
20
|
"fedify",
|
|
@@ -58,44 +55,44 @@ const nodeInfoCommand = command("nodeinfo", merge(object("Arguments", {
|
|
|
58
55
|
|
|
59
56
|
The argument is the hostname of the remote node, or the URL of the remote node.`
|
|
60
57
|
});
|
|
61
|
-
async function runNodeInfo(command
|
|
58
|
+
async function runNodeInfo(command) {
|
|
62
59
|
const spinner = ora({
|
|
63
60
|
text: "Fetching a NodeInfo document...",
|
|
64
61
|
discardStdin: false
|
|
65
62
|
}).start();
|
|
66
|
-
const url = new URL(URL.canParse(command
|
|
67
|
-
if (command
|
|
68
|
-
const nodeInfo
|
|
63
|
+
const url = new URL(URL.canParse(command.host) ? command.host : `https://${command.host}`);
|
|
64
|
+
if (command.raw) {
|
|
65
|
+
const nodeInfo = await getNodeInfo(url, {
|
|
69
66
|
parse: "none",
|
|
70
|
-
userAgent: command
|
|
67
|
+
userAgent: command.userAgent
|
|
71
68
|
});
|
|
72
|
-
if (nodeInfo
|
|
69
|
+
if (nodeInfo === void 0) {
|
|
73
70
|
spinner.fail("No NodeInfo document found.");
|
|
74
71
|
printError(message`No NodeInfo document found.`);
|
|
75
72
|
process.exit(1);
|
|
76
73
|
}
|
|
77
74
|
spinner.succeed("NodeInfo document fetched.");
|
|
78
|
-
print(message`${text(formatObject(nodeInfo
|
|
75
|
+
print(message`${text(formatObject(nodeInfo, void 0, true))}`);
|
|
79
76
|
return;
|
|
80
77
|
}
|
|
81
78
|
const nodeInfo = await getNodeInfo(url, {
|
|
82
|
-
parse: command
|
|
83
|
-
userAgent: command
|
|
79
|
+
parse: command.bestEffort ? "best-effort" : "strict",
|
|
80
|
+
userAgent: command.userAgent
|
|
84
81
|
});
|
|
85
82
|
logger.debug("NodeInfo document: {nodeInfo}", { nodeInfo });
|
|
86
83
|
if (nodeInfo == void 0) {
|
|
87
84
|
spinner.fail("No NodeInfo document found or it is invalid.");
|
|
88
85
|
printError(message`No NodeInfo document found or it is invalid.`);
|
|
89
|
-
if (!command
|
|
86
|
+
if (!command.bestEffort) printError(message`Use the -b/--best-effort option to try to parse the document anyway.`);
|
|
90
87
|
process.exit(1);
|
|
91
88
|
}
|
|
92
89
|
let layout;
|
|
93
90
|
let defaultWidth = 0;
|
|
94
|
-
if (!command
|
|
91
|
+
if (!command.noFavicon) {
|
|
95
92
|
spinner.text = "Fetching the favicon...";
|
|
96
93
|
try {
|
|
97
|
-
const faviconUrl = await getFaviconUrl(url, command
|
|
98
|
-
const response = await fetch(faviconUrl, { headers: { "User-Agent": command
|
|
94
|
+
const faviconUrl = await getFaviconUrl(url, command.userAgent);
|
|
95
|
+
const response = await fetch(faviconUrl, { headers: { "User-Agent": command.userAgent == null ? getUserAgent() : command.userAgent } });
|
|
99
96
|
if (response.ok) {
|
|
100
97
|
const contentType = response.headers.get("Content-Type");
|
|
101
98
|
let buffer = await response.arrayBuffer();
|
|
@@ -104,9 +101,7 @@ async function runNodeInfo(command$1) {
|
|
|
104
101
|
if (images.length < 1) throw new Error("No images found in the ICO file.");
|
|
105
102
|
buffer = images[0].buffer;
|
|
106
103
|
}
|
|
107
|
-
|
|
108
|
-
const colorSupport = checkTerminalColorSupport();
|
|
109
|
-
layout = getAsciiArt(image, DEFAULT_IMAGE_WIDTH, colorSupport, colors).split("\n").map((line) => ` ${line} `);
|
|
104
|
+
layout = getAsciiArt(await Jimp.read(buffer), DEFAULT_IMAGE_WIDTH, checkTerminalColorSupport(), colors).split("\n").map((line) => ` ${line} `);
|
|
110
105
|
defaultWidth = 41;
|
|
111
106
|
} else {
|
|
112
107
|
logger.error("Failed to fetch the favicon: {status} {statusText}", {
|
|
@@ -164,25 +159,25 @@ async function runNodeInfo(command$1) {
|
|
|
164
159
|
layout[next()] += colors.bold(colors.dim("Open registrations:"));
|
|
165
160
|
layout[next()] += " " + (nodeInfo.openRegistrations ? "Yes" : "No");
|
|
166
161
|
}
|
|
167
|
-
if (command
|
|
162
|
+
if (command.metadata && nodeInfo.metadata != null && Object.keys(nodeInfo.metadata).length > 0) {
|
|
168
163
|
layout[next()] += colors.bold(colors.dim("Metadata:"));
|
|
169
|
-
for (const [key, value
|
|
164
|
+
for (const [key, value] of Object.entries(nodeInfo.metadata)) layout[next()] += ` ${colors.dim(key + ":")} ${indent(typeof value === "string" ? value : formatObject(value), defaultWidth + 4 + key.length)}`;
|
|
170
165
|
}
|
|
171
166
|
console.log(layout.join("\n"));
|
|
172
167
|
}
|
|
173
|
-
function indent(text
|
|
174
|
-
return text
|
|
168
|
+
function indent(text, depth) {
|
|
169
|
+
return text.replace(/\n/g, "\n" + " ".repeat(depth));
|
|
175
170
|
}
|
|
176
171
|
const LINK_REGEXP = /<link((?:\s+(?:[-a-z]+)=(?:"[^"]*"|'[^']*'|[^\s]+))*)\s*\/?>/gi;
|
|
177
172
|
const LINK_ATTRS_REGEXP = /(?:\s+([-a-z]+)=("[^"]*"|'[^']*'|[^\s]+))/gi;
|
|
178
173
|
async function getFaviconUrl(url, userAgent) {
|
|
179
174
|
const response = await fetch(url, { headers: { "User-Agent": userAgent == null ? getUserAgent() : userAgent } });
|
|
180
|
-
const text
|
|
181
|
-
for (const match of text
|
|
175
|
+
const text = await response.text();
|
|
176
|
+
for (const match of text.matchAll(LINK_REGEXP)) {
|
|
182
177
|
const attrs = {};
|
|
183
178
|
for (const attrMatch of match[1].matchAll(LINK_ATTRS_REGEXP)) {
|
|
184
|
-
const [, key, value
|
|
185
|
-
attrs[key] = value
|
|
179
|
+
const [, key, value] = attrMatch;
|
|
180
|
+
attrs[key] = value.startsWith("\"") || value.startsWith("'") ? value.slice(1, -1) : value;
|
|
186
181
|
}
|
|
187
182
|
const rel = attrs.rel?.toLowerCase()?.trim()?.split(/\s+/) ?? [];
|
|
188
183
|
if (!rel.includes("icon") && !rel.includes("apple-touch-icon")) continue;
|
|
@@ -220,11 +215,11 @@ const CUBE_VALUES = [
|
|
|
220
215
|
215,
|
|
221
216
|
255
|
|
222
217
|
];
|
|
223
|
-
const findClosestIndex = (value
|
|
218
|
+
const findClosestIndex = (value) => {
|
|
224
219
|
let minDiff = Infinity;
|
|
225
220
|
let closestIndex = 0;
|
|
226
221
|
for (let idx = 0; idx < CUBE_VALUES.length; idx++) {
|
|
227
|
-
const diff = Math.abs(value
|
|
222
|
+
const diff = Math.abs(value - CUBE_VALUES[idx]);
|
|
228
223
|
if (diff < minDiff) {
|
|
229
224
|
minDiff = diff;
|
|
230
225
|
closestIndex = idx;
|
|
@@ -234,10 +229,8 @@ const findClosestIndex = (value$1) => {
|
|
|
234
229
|
};
|
|
235
230
|
function rgbTo256Color(r, g, b) {
|
|
236
231
|
const gray = Math.round((r + g + b) / 3);
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const isExactCubeValue = CUBE_VALUES.includes(r) && r === g && g === b;
|
|
240
|
-
if (!isExactCubeValue) {
|
|
232
|
+
if (Math.abs(r - gray) <= 5 && Math.abs(g - gray) <= 5 && Math.abs(b - gray) <= 5) {
|
|
233
|
+
if (!(CUBE_VALUES.includes(r) && r === g && g === b)) {
|
|
241
234
|
if (gray < 8) return 232;
|
|
242
235
|
if (gray > 238) return 255;
|
|
243
236
|
const grayIndex = Math.round((gray - 8) / 10);
|
|
@@ -249,7 +242,7 @@ function rgbTo256Color(r, g, b) {
|
|
|
249
242
|
const b6 = findClosestIndex(b);
|
|
250
243
|
return 16 + 36 * r6 + 6 * g6 + b6;
|
|
251
244
|
}
|
|
252
|
-
function getAsciiArt(image, width = DEFAULT_IMAGE_WIDTH, colorSupport, colors
|
|
245
|
+
function getAsciiArt(image, width = DEFAULT_IMAGE_WIDTH, colorSupport, colors) {
|
|
253
246
|
const ratio = image.width / image.height;
|
|
254
247
|
const height = Math.round(width / ratio * .5);
|
|
255
248
|
image.resize({
|
|
@@ -259,25 +252,22 @@ function getAsciiArt(image, width = DEFAULT_IMAGE_WIDTH, colorSupport, colors$1)
|
|
|
259
252
|
let art = "";
|
|
260
253
|
for (let y = 0; y < height; y++) {
|
|
261
254
|
for (let x = 0; x < width; x++) {
|
|
262
|
-
const
|
|
263
|
-
const color = intToRGBA(pixel);
|
|
255
|
+
const color = intToRGBA(image.getPixelColor(x, y));
|
|
264
256
|
if (color.a < 1) {
|
|
265
257
|
art += " ";
|
|
266
258
|
continue;
|
|
267
259
|
}
|
|
268
260
|
const brightness = (color.r + color.g + color.b) / 3;
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
if (colorSupport === "truecolor") art += colors$1.rgb(color.r, color.g, color.b)(char);
|
|
261
|
+
const char = ASCII_CHARS[Math.round(brightness / 255 * 72)];
|
|
262
|
+
if (colorSupport === "truecolor") art += colors.rgb(color.r, color.g, color.b)(char);
|
|
272
263
|
else if (colorSupport === "256color") {
|
|
273
264
|
const colorIndex = rgbTo256Color(color.r, color.g, color.b);
|
|
274
|
-
art += colors
|
|
265
|
+
art += colors.ansi256(colorIndex)(char);
|
|
275
266
|
} else art += char;
|
|
276
267
|
}
|
|
277
268
|
if (y < height - 1) art += "\n";
|
|
278
269
|
}
|
|
279
270
|
return art;
|
|
280
271
|
}
|
|
281
|
-
|
|
282
272
|
//#endregion
|
|
283
|
-
export { Jimp, nodeInfoCommand, runNodeInfo };
|
|
273
|
+
export { Jimp, nodeInfoCommand, runNodeInfo };
|
package/dist/options.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
import { Temporal } from "@js-temporal/polyfill";
|
|
3
|
-
|
|
1
|
+
import "@js-temporal/polyfill";
|
|
4
2
|
import { configContext } from "./config.js";
|
|
5
3
|
import { choice, constant, flag, map, merge, message, object, option, or, string, withDefault } from "@optique/core";
|
|
6
4
|
import { bindConfig } from "@optique/config";
|
|
7
5
|
import { getUserAgent } from "@fedify/vocab-runtime";
|
|
8
|
-
|
|
9
6
|
//#region src/options.ts
|
|
10
7
|
/**
|
|
11
8
|
* Available tunneling services for exposing local servers to the public internet.
|
|
@@ -18,8 +15,8 @@ const TUNNEL_SERVICES = [
|
|
|
18
15
|
/**
|
|
19
16
|
* Creates a tunnel service option with customizable option names.
|
|
20
17
|
*/
|
|
21
|
-
function createTunnelServiceOption(optionNames
|
|
22
|
-
return withDefault(bindConfig(option(...optionNames
|
|
18
|
+
function createTunnelServiceOption(optionNames = ["--tunnel-service"]) {
|
|
19
|
+
return withDefault(bindConfig(option(...optionNames, choice(TUNNEL_SERVICES, { metavar: "SERVICE" }), { description: message`The tunneling service to use.
|
|
23
20
|
By default, any of the supported tunneling services will be used
|
|
24
21
|
(randomly selected for each tunnel).` }), {
|
|
25
22
|
context: configContext,
|
|
@@ -54,31 +51,17 @@ const userAgentOption = object({ userAgent: bindConfig(option("-u", "--user-agen
|
|
|
54
51
|
default: getUserAgent()
|
|
55
52
|
}) });
|
|
56
53
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* These options are mutually exclusive:
|
|
60
|
-
* - `--config PATH` loads an additional config file on top of standard hierarchy
|
|
61
|
-
* - `--ignore-config` skips all config files (useful for CI reproducibility)
|
|
54
|
+
* Global options that apply to all commands.
|
|
62
55
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* - `{ ignoreConfig: false, configPath: string }` when `--config` is specified
|
|
66
|
-
* - `{ ignoreConfig: false }` when neither is specified (default)
|
|
56
|
+
* Combines debug mode and configuration file options into a single
|
|
57
|
+
* "Global options" group.
|
|
67
58
|
*/
|
|
68
|
-
const
|
|
59
|
+
const globalOptions = merge("Global options", debugOption, withDefault(or(object({ ignoreConfig: map(flag("--ignore-config", { description: message`Ignore all configuration files.` }), () => true) }), object({
|
|
69
60
|
ignoreConfig: constant(false),
|
|
70
61
|
configPath: option("--config", string({ metavar: "PATH" }), { description: message`Load an additional configuration file.` })
|
|
71
62
|
})), {
|
|
72
63
|
ignoreConfig: false,
|
|
73
64
|
configPath: void 0
|
|
74
|
-
});
|
|
75
|
-
/**
|
|
76
|
-
* Global options that apply to all commands.
|
|
77
|
-
*
|
|
78
|
-
* Combines debug mode and configuration file options into a single
|
|
79
|
-
* "Global options" group.
|
|
80
|
-
*/
|
|
81
|
-
const globalOptions = merge("Global options", debugOption, configOption);
|
|
82
|
-
|
|
65
|
+
}));
|
|
83
66
|
//#endregion
|
|
84
|
-
export { createTunnelOption, createTunnelServiceOption, globalOptions, userAgentOption };
|
|
67
|
+
export { createTunnelOption, createTunnelServiceOption, globalOptions, userAgentOption };
|