@fedify/cli 2.3.0-dev.1299 → 2.3.0-dev.1344

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 (57) hide show
  1. package/dist/bench/action.js +189 -29
  2. package/dist/bench/command.js +43 -13
  3. package/dist/bench/compare/schema.js +16 -0
  4. package/dist/bench/compare.js +667 -0
  5. package/dist/bench/load/clock.js +20 -2
  6. package/dist/bench/load/generator.js +42 -9
  7. package/dist/bench/metrics/stats-client.js +65 -3
  8. package/dist/bench/mod.js +9 -2
  9. package/dist/bench/render/markdown.js +1 -0
  10. package/dist/bench/render/text.js +1 -0
  11. package/dist/bench/result/build.js +133 -10
  12. package/dist/bench/result/expect/evaluate.js +1 -1
  13. package/dist/bench/result/schema.js +353 -3
  14. package/dist/bench/safety/gate.js +4 -2
  15. package/dist/bench/scenario/normalize.js +1 -2
  16. package/dist/bench/scenario/schema.js +50 -9
  17. package/dist/bench/scenario/validate.js +2 -2
  18. package/dist/bench/scenarios/actor.js +38 -0
  19. package/dist/bench/scenarios/failure.js +363 -0
  20. package/dist/bench/scenarios/fanout.js +261 -0
  21. package/dist/bench/scenarios/inbox.js +4 -12
  22. package/dist/bench/scenarios/mixed.js +244 -0
  23. package/dist/bench/scenarios/object-discovery.js +211 -0
  24. package/dist/bench/scenarios/object.js +54 -0
  25. package/dist/bench/scenarios/read.js +108 -0
  26. package/dist/bench/scenarios/registry.js +19 -1
  27. package/dist/bench/scenarios/runner.js +21 -1
  28. package/dist/bench/scenarios/webfinger.js +1 -1
  29. package/dist/cache.js +1 -1
  30. package/dist/commands.js +110 -0
  31. package/dist/config.js +1 -1
  32. package/dist/deno.js +1 -1
  33. package/dist/docloader.js +1 -1
  34. package/dist/generate-vocab/action.js +1 -1
  35. package/dist/generate-vocab/command.js +5 -3
  36. package/dist/imagerenderer.js +2 -2
  37. package/dist/inbox/command.js +6 -4
  38. package/dist/inbox.js +4 -4
  39. package/dist/log.js +2 -2
  40. package/dist/lookup/command.js +121 -0
  41. package/dist/lookup.js +12 -123
  42. package/dist/mod.js +2 -23
  43. package/dist/nodeinfo.js +11 -9
  44. package/dist/options.js +1 -1
  45. package/dist/relay/command.js +6 -4
  46. package/dist/relay.js +2 -2
  47. package/dist/runner.js +69 -46
  48. package/dist/tempserver.js +1 -1
  49. package/dist/tunnel.js +6 -4
  50. package/dist/utils.js +5 -4
  51. package/dist/webfinger/action.js +1 -1
  52. package/dist/webfinger/command.js +6 -4
  53. package/dist/webfinger/lib.js +1 -1
  54. package/package.json +13 -12
  55. package/dist/generate-vocab/mod.js +0 -4
  56. package/dist/init/mod.js +0 -3
  57. package/dist/webfinger/mod.js +0 -4
@@ -29,7 +29,7 @@ const webfingerRunner = { async run(context) {
29
29
  baseline = await fetchServerSnapshot(context.target, fetchImpl);
30
30
  baselineTaken = true;
31
31
  }, rawSend);
32
- const measurement = aggregateSamples((await runLoad(loadPlanOf(context.scenario, context.rng), send, context.clock)).samples, {
32
+ const measurement = aggregateSamples((await runLoad(loadPlanOf(context.scenario, context.rng), send, context.clock, context.signal)).samples, {
33
33
  measuredWindowMs: measuredWindowMs(context.scenario),
34
34
  includeHistogram: true
35
35
  });
package/dist/cache.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import "@js-temporal/polyfill";
2
- import { mkdir } from "node:fs/promises";
3
2
  import process from "node:process";
4
3
  import { homedir } from "node:os";
5
4
  import { join } from "node:path";
5
+ import { mkdir } from "node:fs/promises";
6
6
  //#region src/cache.ts
7
7
  /**
8
8
  * Returns the default cache directory path.
@@ -0,0 +1,110 @@
1
+ import "@js-temporal/polyfill";
2
+ import { benchMetadata, benchOptions } from "./bench/command.js";
3
+ import { generateVocabMetadata, generateVocabOptions } from "./generate-vocab/command.js";
4
+ import { inboxMetadata, inboxOptions } from "./inbox/command.js";
5
+ import { lookupMetadata, lookupOptions } from "./lookup/command.js";
6
+ import { nodeInfoMetadata, nodeInfoOptions, runNodeInfo } from "./nodeinfo.js";
7
+ import { relayMetadata, relayOptions } from "./relay/command.js";
8
+ import { runTunnel, tunnelMetadata, tunnelOptions } from "./tunnel.js";
9
+ import { webFingerMetadata, webFingerOptions } from "./webfinger/command.js";
10
+ import { constant, merge, message, object, optionNames } from "@optique/core";
11
+ import { initOptions, runInit } from "@fedify/init";
12
+ import { defineCommand } from "@optique/discover";
13
+ //#region src/commands.ts
14
+ function defineCliCommand(command) {
15
+ const { run, ...definition } = command;
16
+ return {
17
+ ...defineCommand({
18
+ ...definition,
19
+ handler: (_value) => {}
20
+ }),
21
+ run
22
+ };
23
+ }
24
+ const generatingCommands = [defineCliCommand({
25
+ path: ["init"],
26
+ parser: merge(initOptions, object({ command: constant("init") })),
27
+ metadata: {
28
+ brief: message`Initialize a new Fedify project directory.`,
29
+ description: message`Initialize a new Fedify project directory.
30
+
31
+ By default, it initializes the current directory. You can specify a different directory as an argument.
32
+
33
+ Unless you specify all options (${optionNames(["-w", "--web-framework"])}, ${optionNames(["-p", "--package-manager"])}, ${optionNames(["-k", "--kv-store"])}, and ${optionNames(["-m", "--message-queue"])}), it will prompt you to select the options interactively.`
34
+ },
35
+ run: runInit
36
+ }), defineCliCommand({
37
+ path: ["generate-vocab"],
38
+ parser: generateVocabOptions,
39
+ metadata: generateVocabMetadata,
40
+ run: async (value) => {
41
+ const { default: runGenerateVocab } = await import("./generate-vocab/action.js");
42
+ return await runGenerateVocab(value);
43
+ }
44
+ })];
45
+ const activityPubCommands = [
46
+ defineCliCommand({
47
+ path: ["webfinger"],
48
+ parser: webFingerOptions,
49
+ metadata: webFingerMetadata,
50
+ run: async (value) => {
51
+ const { default: runWebFinger } = await import("./webfinger/action.js");
52
+ return await runWebFinger(value);
53
+ }
54
+ }),
55
+ defineCliCommand({
56
+ path: ["lookup"],
57
+ parser: lookupOptions,
58
+ metadata: lookupMetadata,
59
+ run: async (value) => {
60
+ const { runLookup } = await import("./lookup.js");
61
+ return await runLookup(value);
62
+ }
63
+ }),
64
+ defineCliCommand({
65
+ path: ["inbox"],
66
+ parser: inboxOptions,
67
+ metadata: inboxMetadata,
68
+ run: async (value) => {
69
+ const { runInbox } = await import("./inbox.js");
70
+ return await runInbox(value);
71
+ }
72
+ }),
73
+ defineCliCommand({
74
+ path: ["nodeinfo"],
75
+ parser: nodeInfoOptions,
76
+ metadata: nodeInfoMetadata,
77
+ run: runNodeInfo
78
+ }),
79
+ defineCliCommand({
80
+ path: ["relay"],
81
+ parser: relayOptions,
82
+ metadata: relayMetadata,
83
+ run: async (value) => {
84
+ const { runRelay } = await import("./relay.js");
85
+ return await runRelay(value);
86
+ }
87
+ }),
88
+ defineCliCommand({
89
+ path: ["bench"],
90
+ parser: benchOptions,
91
+ metadata: benchMetadata,
92
+ run: async (value) => {
93
+ const { runBench } = await import("./bench/mod.js");
94
+ return await runBench(value);
95
+ }
96
+ })
97
+ ];
98
+ const networkCommands = [defineCliCommand({
99
+ path: ["tunnel"],
100
+ parser: tunnelOptions,
101
+ metadata: tunnelMetadata,
102
+ run: runTunnel
103
+ })];
104
+ [
105
+ ...generatingCommands,
106
+ ...activityPubCommands,
107
+ ...networkCommands
108
+ ];
109
+ //#endregion
110
+ export { activityPubCommands, generatingCommands, networkCommands };
package/dist/config.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import { message } from "@optique/core";
3
3
  import { printError } from "@optique/run";
4
- import { createConfigContext } from "@optique/config";
5
4
  import { readFileSync } from "node:fs";
6
5
  import { parse } from "smol-toml";
6
+ import { createConfigContext } from "@optique/config";
7
7
  import { array, boolean, check, forward, integer as integer$1, minValue, number, object as object$1, optional as optional$1, picklist, pipe, string as string$1 } from "valibot";
8
8
  //#region src/config.ts
9
9
  /**
package/dist/deno.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "@js-temporal/polyfill";
2
2
  //#region deno.json
3
- var version = "2.3.0-dev.1299+9c399256";
3
+ var version = "2.3.0-dev.1344+d573d413";
4
4
  //#endregion
5
5
  export { version };
package/dist/docloader.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import "@js-temporal/polyfill";
2
- import { kvCache } from "@fedify/fedify";
3
2
  import { getDocumentLoader } from "@fedify/vocab-runtime";
3
+ import { kvCache } from "@fedify/fedify";
4
4
  import { getKvStore } from "#kv";
5
5
  //#region src/docloader.ts
6
6
  const documentLoaders = {};
@@ -1,7 +1,7 @@
1
1
  import "@js-temporal/polyfill";
2
- import { stat } from "node:fs/promises";
3
2
  import process from "node:process";
4
3
  import { printError } from "@optique/run";
4
+ import { stat } from "node:fs/promises";
5
5
  import { generateVocab } from "@fedify/vocab-tools";
6
6
  import { message } from "@optique/core/message";
7
7
  //#region src/generate-vocab/action.ts
@@ -12,10 +12,12 @@ const generatedPath = argument(path({
12
12
  type: "file",
13
13
  allowCreate: true
14
14
  }), { description: message`Path to output the generated vocabulary classes. Should end with ${".ts"} suffix.` });
15
- const generateVocabCommand = command("generate-vocab", object("Generation options", {
15
+ const generateVocabOptions = object("Generation options", {
16
16
  command: constant("generate-vocab"),
17
17
  schemaDir,
18
18
  generatedPath
19
- }), { description: message`Generate vocabulary classes from schema files.` });
19
+ });
20
+ const generateVocabMetadata = { description: message`Generate vocabulary classes from schema files.` };
21
+ command("generate-vocab", generateVocabOptions, generateVocabMetadata);
20
22
  //#endregion
21
- export { generateVocabCommand as default };
23
+ export { generateVocabMetadata, generateVocabOptions };
@@ -1,10 +1,10 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import { Jimp } from "./nodeinfo.js";
3
- import fs from "node:fs/promises";
4
3
  import process from "node:process";
5
- import { validatePublicUrl } from "@fedify/vocab-runtime";
6
4
  import os from "node:os";
7
5
  import path from "node:path";
6
+ import { validatePublicUrl } from "@fedify/vocab-runtime";
7
+ import fs from "node:fs/promises";
8
8
  import { encodeBase64 } from "byte-encodings/base64";
9
9
  //#region src/imagerenderer.ts
10
10
  const KITTY_IDENTIFIERS = [
@@ -6,7 +6,7 @@ import { bindConfig } from "@optique/config";
6
6
  //#region src/inbox/command.ts
7
7
  const DEFAULT_EPHEMERAL_INBOX_NAME = "Fedify Ephemeral Inbox";
8
8
  const DEFAULT_EPHEMERAL_INBOX_SUMMARY = "An ephemeral ActivityPub inbox for testing purposes.";
9
- const inboxCommand = command("inbox", merge(object("Inbox options", {
9
+ const inboxOptions = merge(object("Inbox options", {
10
10
  command: constant("inbox"),
11
11
  follow: bindConfig(multiple(option("-f", "--follow", string({ metavar: "URI" }), { description: message`Follow the given actor. The argument can be either an actor URI or a handle. Can be specified multiple times.` })), {
12
12
  context: configContext,
@@ -33,9 +33,11 @@ const inboxCommand = command("inbox", merge(object("Inbox options", {
33
33
  key: (config) => config.inbox?.authorizedFetch ?? false,
34
34
  default: false
35
35
  })
36
- }), group("Tunnel options", createTunnelOption("inbox"))), {
36
+ }), group("Tunnel options", createTunnelOption("inbox")));
37
+ const inboxMetadata = {
37
38
  brief: message`Run an ephemeral ActivityPub inbox server.`,
38
39
  description: message`Spins up an ephemeral server that serves the ActivityPub inbox with a one-time actor, through a short-lived public DNS with HTTPS. You can monitor the incoming activities in real-time.`
39
- });
40
+ };
41
+ command("inbox", inboxOptions, inboxMetadata);
40
42
  //#endregion
41
- export { inboxCommand };
43
+ export { inboxMetadata, inboxOptions };
package/dist/inbox.js CHANGED
@@ -1,18 +1,18 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
- import { getDocumentLoader } from "./docloader.js";
3
2
  import { colors, matchesActor } from "./utils.js";
3
+ import { configureLogging, recordingSink } from "./log.js";
4
4
  import { version } from "./deno.js";
5
+ import { getDocumentLoader } from "./docloader.js";
5
6
  import { ActivityEntryPage, ActivityListPage } from "./inbox/view.js";
6
- import { configureLogging, recordingSink } from "./log.js";
7
7
  import { tableStyle } from "./table.js";
8
8
  import { spawnTemporaryServer } from "./tempserver.js";
9
9
  import process from "node:process";
10
10
  import { MemoryKvStore, createFederation, generateCryptoKeyPair } from "@fedify/fedify";
11
- import { Accept, Activity, Application, Delete, Endpoints, Follow, Image, PUBLIC_COLLECTION, isActor, lookupObject } from "@fedify/vocab";
12
11
  import { getLogger } from "@logtape/logtape";
12
+ import ora from "ora";
13
+ import { Accept, Activity, Application, Delete, Endpoints, Follow, Image, PUBLIC_COLLECTION, isActor, lookupObject } from "@fedify/vocab";
13
14
  import Table from "cli-table3";
14
15
  import { Hono } from "hono";
15
- import ora from "ora";
16
16
  import { jsx } from "hono/jsx/jsx-runtime";
17
17
  //#region src/inbox.tsx
18
18
  /** @jsx react-jsx */
package/dist/log.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import "@js-temporal/polyfill";
2
- import { mkdir } from "node:fs/promises";
3
2
  import process from "node:process";
3
+ import { dirname } from "node:path";
4
4
  import { configure, getConsoleSink } from "@logtape/logtape";
5
+ import { mkdir } from "node:fs/promises";
5
6
  import { getFileSink } from "@logtape/file";
6
7
  import { AsyncLocalStorage } from "node:async_hooks";
7
- import { dirname } from "node:path";
8
8
  //#region src/log.ts
9
9
  function getRecordingSink() {
10
10
  let records = [];
@@ -0,0 +1,121 @@
1
+ import "@js-temporal/polyfill";
2
+ import { configContext } from "../config.js";
3
+ import { createTunnelServiceOption, userAgentOption } from "../options.js";
4
+ import { argument, choice, command, constant, flag, float, integer, map, merge, message, multiple, object, option, optionNames, optional, or, string, withDefault } from "@optique/core";
5
+ import { path } from "@optique/run";
6
+ import { bindConfig } from "@optique/config";
7
+ //#region src/lookup/command.ts
8
+ const IN_REPLY_TO_IRI = "https://www.w3.org/ns/activitystreams#inReplyTo";
9
+ const QUOTE_IRI = "https://w3id.org/fep/044f#quote";
10
+ const QUOTE_URL_IRI = "https://www.w3.org/ns/activitystreams#quoteUrl";
11
+ const MISSKEY_QUOTE_IRI = "https://misskey-hub.net/ns#_misskey_quote";
12
+ const FEDIBIRD_QUOTE_IRI = "http://fedibird.com/ns#quoteUri";
13
+ const recurseProperties = [
14
+ "replyTarget",
15
+ "quote",
16
+ "quoteUrl",
17
+ IN_REPLY_TO_IRI,
18
+ QUOTE_IRI,
19
+ QUOTE_URL_IRI,
20
+ MISSKEY_QUOTE_IRI,
21
+ FEDIBIRD_QUOTE_IRI
22
+ ];
23
+ const suppressErrorsOption = bindConfig(flag("-S", "--suppress-errors", { description: message`Suppress partial errors during traversal or recursion.` }), {
24
+ context: configContext,
25
+ key: (config) => config.lookup?.suppressErrors ?? false,
26
+ default: false
27
+ });
28
+ const allowPrivateAddressOption = bindConfig(flag("-p", "--allow-private-address", { description: message`Allow private IP addresses for URLs discovered \
29
+ during traversal or recursive object fetches. Recursive JSON-LD \
30
+ context URLs always remain blocked. URLs explicitly provided on the \
31
+ command line always allow private addresses.` }), {
32
+ context: configContext,
33
+ key: (config) => config.lookup?.allowPrivateAddress ?? false,
34
+ default: false
35
+ });
36
+ const authorizedFetchOption = withDefault(object("Authorized fetch options", {
37
+ authorizedFetch: bindConfig(map(flag("-a", "--authorized-fetch", { description: message`Sign the request with an one-time key.` }), () => true), {
38
+ context: configContext,
39
+ key: (config) => config.lookup?.authorizedFetch ? true : void 0
40
+ }),
41
+ firstKnock: bindConfig(option("--first-knock", choice(["draft-cavage-http-signatures-12", "rfc9421"]), { description: message`The first-knock spec for ${optionNames(["-a", "--authorized-fetch"])}. It is used for the double-knocking technique.` }), {
42
+ context: configContext,
43
+ key: (config) => config.lookup?.firstKnock ?? "draft-cavage-http-signatures-12",
44
+ default: "draft-cavage-http-signatures-12"
45
+ }),
46
+ tunnelService: optional(createTunnelServiceOption())
47
+ }), {
48
+ authorizedFetch: false,
49
+ firstKnock: void 0,
50
+ tunnelService: void 0
51
+ });
52
+ const lookupModeOption = withDefault(or(object("Recurse options", {
53
+ traverse: constant(false),
54
+ recurse: bindConfig(option("--recurse", choice(recurseProperties, { metavar: "PROPERTY" }), { description: message`Recursively follow a relationship property.` }), {
55
+ context: configContext,
56
+ key: (config) => config.lookup?.recurse
57
+ }),
58
+ recurseDepth: bindConfig(option("--recurse-depth", integer({
59
+ min: 1,
60
+ metavar: "DEPTH"
61
+ }), { description: message`Maximum recursion depth for ${optionNames(["--recurse"])}.` }), {
62
+ context: configContext,
63
+ key: (config) => config.lookup?.recurseDepth,
64
+ default: 20
65
+ }),
66
+ suppressErrors: suppressErrorsOption
67
+ }), object("Traverse options", {
68
+ traverse: bindConfig(flag("-t", "--traverse", { description: message`Traverse the given collection(s) to fetch all items.` }), {
69
+ context: configContext,
70
+ key: (config) => config.lookup?.traverse ?? false,
71
+ default: false
72
+ }),
73
+ recurse: constant(void 0),
74
+ recurseDepth: constant(void 0),
75
+ suppressErrors: suppressErrorsOption
76
+ })), {
77
+ traverse: false,
78
+ recurse: void 0,
79
+ recurseDepth: void 0,
80
+ suppressErrors: false
81
+ });
82
+ const lookupOptions = merge(object({ command: constant("lookup") }), lookupModeOption, authorizedFetchOption, merge("Network options", userAgentOption, object({
83
+ allowPrivateAddress: allowPrivateAddressOption,
84
+ timeout: optional(bindConfig(option("-T", "--timeout", float({
85
+ min: 0,
86
+ metavar: "SECONDS"
87
+ }), { description: message`Set timeout for network requests in seconds.` }), {
88
+ context: configContext,
89
+ key: (config) => config.lookup?.timeout
90
+ }))
91
+ })), object("Arguments", { urls: multiple(argument(string({ metavar: "URL_OR_HANDLE" }), { description: message`One or more URLs or handles to look up.` }), { min: 1 }) }), object("Output options", {
92
+ reverse: bindConfig(flag("--reverse", { description: message`Reverse the output order of fetched objects or items.` }), {
93
+ context: configContext,
94
+ key: (config) => config.lookup?.reverse ?? false,
95
+ default: false
96
+ }),
97
+ format: bindConfig(optional(or(map(flag("-r", "--raw", { description: message`Print the fetched JSON-LD document as is.` }), () => "raw"), map(flag("-C", "--compact", { description: message`Compact the fetched JSON-LD document.` }), () => "compact"), map(flag("-e", "--expand", { description: message`Expand the fetched JSON-LD document.` }), () => "expand"))), {
98
+ context: configContext,
99
+ key: (config) => config.lookup?.defaultFormat ?? "default",
100
+ default: "default"
101
+ }),
102
+ separator: bindConfig(option("-s", "--separator", string({ metavar: "SEPARATOR" }), { description: message`Specify the separator between adjacent output objects or collection items.` }), {
103
+ context: configContext,
104
+ key: (config) => config.lookup?.separator ?? "----",
105
+ default: "----"
106
+ }),
107
+ output: optional(option("-o", "--output", path({
108
+ metavar: "OUTPUT_PATH",
109
+ type: "file",
110
+ allowCreate: true
111
+ }), { description: message`Specify the output file path.` }))
112
+ }));
113
+ const lookupMetadata = {
114
+ brief: message`Look up Activity Streams objects.`,
115
+ description: message`Look up Activity Streams objects by URL or actor handle.
116
+
117
+ The arguments can be either URLs or actor handles (e.g., ${"@username@domain"}), and they can be multiple.`
118
+ };
119
+ command("lookup", lookupOptions, lookupMetadata);
120
+ //#endregion
121
+ export { FEDIBIRD_QUOTE_IRI, IN_REPLY_TO_IRI, MISSKEY_QUOTE_IRI, QUOTE_IRI, QUOTE_URL_IRI, authorizedFetchOption, lookupMetadata, lookupOptions };
package/dist/lookup.js CHANGED
@@ -1,22 +1,20 @@
1
1
  import "@js-temporal/polyfill";
2
- import { getContextLoader, getDocumentLoader as getDocumentLoader$1 } from "./docloader.js";
2
+ import { FEDIBIRD_QUOTE_IRI, IN_REPLY_TO_IRI, MISSKEY_QUOTE_IRI, QUOTE_IRI, QUOTE_URL_IRI } from "./lookup/command.js";
3
3
  import { colorEnabled, colors, describeError, formatObject } from "./utils.js";
4
- import { configContext } from "./config.js";
5
- import { createTunnelServiceOption, userAgentOption } from "./options.js";
6
4
  import { configureLogging } from "./log.js";
5
+ import { getContextLoader, getDocumentLoader as getDocumentLoader$1 } from "./docloader.js";
7
6
  import { spawnTemporaryServer } from "./tempserver.js";
8
7
  import { renderImages } from "./imagerenderer.js";
9
8
  import process from "node:process";
10
- import { generateCryptoKeyPair, getAuthenticatedDocumentLoader, respondWithObject } from "@fedify/fedify";
9
+ import { message, optionNames } from "@optique/core";
10
+ import { printError } from "@optique/run";
11
+ import { createWriteStream } from "node:fs";
11
12
  import { UrlError, expandIPv6Address, isValidPublicIPv4Address, isValidPublicIPv6Address } from "@fedify/vocab-runtime";
12
- import { Application, Collection, CryptographicKey, Object as Object$1, lookupObject, traverseCollection } from "@fedify/vocab";
13
- import { argument, choice, command, constant, flag, float, integer, map, merge, message, multiple, object, option, optionNames, optional, or, string, withDefault } from "@optique/core";
14
- import { path, printError } from "@optique/run";
13
+ import { generateCryptoKeyPair, getAuthenticatedDocumentLoader, respondWithObject } from "@fedify/fedify";
15
14
  import { getLogger } from "@logtape/logtape";
16
- import { bindConfig } from "@optique/config";
17
- import { createWriteStream } from "node:fs";
18
- import { url } from "@optique/core/message";
19
15
  import ora from "ora";
16
+ import { Application, Collection, CryptographicKey, Object as Object$1, lookupObject, traverseCollection } from "@fedify/vocab";
17
+ import { url } from "@optique/core/message";
20
18
  import { isIP } from "node:net";
21
19
  //#region src/lookup.ts
22
20
  const logger = getLogger([
@@ -24,116 +22,6 @@ const logger = getLogger([
24
22
  "cli",
25
23
  "lookup"
26
24
  ]);
27
- const IN_REPLY_TO_IRI = "https://www.w3.org/ns/activitystreams#inReplyTo";
28
- const QUOTE_IRI = "https://w3id.org/fep/044f#quote";
29
- const QUOTE_URL_IRI = "https://www.w3.org/ns/activitystreams#quoteUrl";
30
- const MISSKEY_QUOTE_IRI = "https://misskey-hub.net/ns#_misskey_quote";
31
- const FEDIBIRD_QUOTE_IRI = "http://fedibird.com/ns#quoteUri";
32
- const recurseProperties = [
33
- "replyTarget",
34
- "quote",
35
- "quoteUrl",
36
- IN_REPLY_TO_IRI,
37
- QUOTE_IRI,
38
- QUOTE_URL_IRI,
39
- MISSKEY_QUOTE_IRI,
40
- FEDIBIRD_QUOTE_IRI
41
- ];
42
- const suppressErrorsOption = bindConfig(flag("-S", "--suppress-errors", { description: message`Suppress partial errors during traversal or recursion.` }), {
43
- context: configContext,
44
- key: (config) => config.lookup?.suppressErrors ?? false,
45
- default: false
46
- });
47
- const allowPrivateAddressOption = bindConfig(flag("-p", "--allow-private-address", { description: message`Allow private IP addresses for URLs discovered \
48
- during traversal or recursive object fetches. Recursive JSON-LD \
49
- context URLs always remain blocked. URLs explicitly provided on the \
50
- command line always allow private addresses.` }), {
51
- context: configContext,
52
- key: (config) => config.lookup?.allowPrivateAddress ?? false,
53
- default: false
54
- });
55
- const authorizedFetchOption = withDefault(object("Authorized fetch options", {
56
- authorizedFetch: bindConfig(map(flag("-a", "--authorized-fetch", { description: message`Sign the request with an one-time key.` }), () => true), {
57
- context: configContext,
58
- key: (config) => config.lookup?.authorizedFetch ? true : void 0
59
- }),
60
- firstKnock: bindConfig(option("--first-knock", choice(["draft-cavage-http-signatures-12", "rfc9421"]), { description: message`The first-knock spec for ${optionNames(["-a", "--authorized-fetch"])}. It is used for the double-knocking technique.` }), {
61
- context: configContext,
62
- key: (config) => config.lookup?.firstKnock ?? "draft-cavage-http-signatures-12",
63
- default: "draft-cavage-http-signatures-12"
64
- }),
65
- tunnelService: optional(createTunnelServiceOption())
66
- }), {
67
- authorizedFetch: false,
68
- firstKnock: void 0,
69
- tunnelService: void 0
70
- });
71
- const lookupModeOption = withDefault(or(object("Recurse options", {
72
- traverse: constant(false),
73
- recurse: bindConfig(option("--recurse", choice(recurseProperties, { metavar: "PROPERTY" }), { description: message`Recursively follow a relationship property.` }), {
74
- context: configContext,
75
- key: (config) => config.lookup?.recurse
76
- }),
77
- recurseDepth: bindConfig(option("--recurse-depth", integer({
78
- min: 1,
79
- metavar: "DEPTH"
80
- }), { description: message`Maximum recursion depth for ${optionNames(["--recurse"])}.` }), {
81
- context: configContext,
82
- key: (config) => config.lookup?.recurseDepth,
83
- default: 20
84
- }),
85
- suppressErrors: suppressErrorsOption
86
- }), object("Traverse options", {
87
- traverse: bindConfig(flag("-t", "--traverse", { description: message`Traverse the given collection(s) to fetch all items.` }), {
88
- context: configContext,
89
- key: (config) => config.lookup?.traverse ?? false,
90
- default: false
91
- }),
92
- recurse: constant(void 0),
93
- recurseDepth: constant(void 0),
94
- suppressErrors: suppressErrorsOption
95
- })), {
96
- traverse: false,
97
- recurse: void 0,
98
- recurseDepth: void 0,
99
- suppressErrors: false
100
- });
101
- const lookupCommand = command("lookup", merge(object({ command: constant("lookup") }), lookupModeOption, authorizedFetchOption, merge("Network options", userAgentOption, object({
102
- allowPrivateAddress: allowPrivateAddressOption,
103
- timeout: optional(bindConfig(option("-T", "--timeout", float({
104
- min: 0,
105
- metavar: "SECONDS"
106
- }), { description: message`Set timeout for network requests in seconds.` }), {
107
- context: configContext,
108
- key: (config) => config.lookup?.timeout
109
- }))
110
- })), object("Arguments", { urls: multiple(argument(string({ metavar: "URL_OR_HANDLE" }), { description: message`One or more URLs or handles to look up.` }), { min: 1 }) }), object("Output options", {
111
- reverse: bindConfig(flag("--reverse", { description: message`Reverse the output order of fetched objects or items.` }), {
112
- context: configContext,
113
- key: (config) => config.lookup?.reverse ?? false,
114
- default: false
115
- }),
116
- format: bindConfig(optional(or(map(flag("-r", "--raw", { description: message`Print the fetched JSON-LD document as is.` }), () => "raw"), map(flag("-C", "--compact", { description: message`Compact the fetched JSON-LD document.` }), () => "compact"), map(flag("-e", "--expand", { description: message`Expand the fetched JSON-LD document.` }), () => "expand"))), {
117
- context: configContext,
118
- key: (config) => config.lookup?.defaultFormat ?? "default",
119
- default: "default"
120
- }),
121
- separator: bindConfig(option("-s", "--separator", string({ metavar: "SEPARATOR" }), { description: message`Specify the separator between adjacent output objects or collection items.` }), {
122
- context: configContext,
123
- key: (config) => config.lookup?.separator ?? "----",
124
- default: "----"
125
- }),
126
- output: optional(option("-o", "--output", path({
127
- metavar: "OUTPUT_PATH",
128
- type: "file",
129
- allowCreate: true
130
- }), { description: message`Specify the output file path.` }))
131
- })), {
132
- brief: message`Look up Activity Streams objects.`,
133
- description: message`Look up Activity Streams objects by URL or actor handle.
134
-
135
- The arguments can be either URLs or actor handles (e.g., ${"@username@domain"}), and they can be multiple.`
136
- });
137
25
  var TimeoutError = class extends Error {
138
26
  name = "TimeoutError";
139
27
  constructor(message) {
@@ -472,6 +360,7 @@ async function runLookup(command, deps = {}) {
472
360
  effectiveDeps.exit(cleanupFailed && code === 0 ? 1 : code);
473
361
  };
474
362
  if (command.authorizedFetch) {
363
+ const firstKnock = command.firstKnock ?? "draft-cavage-http-signatures-12";
475
364
  spinner.text = "Generating a one-time key pair...";
476
365
  const key = await generateCryptoKeyPair();
477
366
  spinner.text = "Spinning up a temporary ActivityPub server...";
@@ -511,7 +400,7 @@ async function runLookup(command, deps = {}) {
511
400
  userAgent: command.userAgent,
512
401
  specDeterminer: {
513
402
  determineSpec() {
514
- return command.firstKnock;
403
+ return firstKnock;
515
404
  },
516
405
  rememberSpec() {}
517
406
  }
@@ -521,7 +410,7 @@ async function runLookup(command, deps = {}) {
521
410
  userAgent: command.userAgent,
522
411
  specDeterminer: {
523
412
  determineSpec() {
524
- return command.firstKnock;
413
+ return firstKnock;
525
414
  },
526
415
  rememberSpec() {}
527
416
  }
@@ -818,4 +707,4 @@ async function runLookup(command, deps = {}) {
818
707
  if (success && command.output) spinner.succeed(`Successfully wrote output to ${colors.green(command.output)}.`);
819
708
  }
820
709
  //#endregion
821
- export { lookupCommand, runLookup };
710
+ export { runLookup };
package/dist/mod.js CHANGED
@@ -1,31 +1,10 @@
1
1
  #!/usr/bin/env -S node --disable-warning=ExperimentalWarning
2
2
  import "@js-temporal/polyfill";
3
- import runBench from "./bench/action.js";
4
- import "./bench/mod.js";
5
- import runGenerateVocab from "./generate-vocab/action.js";
6
- import "./generate-vocab/mod.js";
7
- import { runInbox } from "./inbox.js";
8
- import { runInit } from "./init/mod.js";
9
- import { runNodeInfo } from "./nodeinfo.js";
10
- import { runLookup } from "./lookup.js";
11
- import { runRelay } from "./relay.js";
12
- import { runTunnel } from "./tunnel.js";
13
- import runWebFinger from "./webfinger/action.js";
14
- import "./webfinger/mod.js";
15
- import { runCli } from "./runner.js";
3
+ import { parseCliProgram } from "./runner.js";
16
4
  import process from "node:process";
17
5
  //#region src/mod.ts
18
6
  async function main() {
19
- const result = await runCli(process.argv.slice(2));
20
- if (result.command === "init") await runInit(result);
21
- else if (result.command === "lookup") await runLookup(result);
22
- else if (result.command === "webfinger") await runWebFinger(result);
23
- else if (result.command === "inbox") await runInbox(result);
24
- else if (result.command === "nodeinfo") await runNodeInfo(result);
25
- else if (result.command === "tunnel") await runTunnel(result);
26
- else if (result.command === "generate-vocab") await runGenerateVocab(result);
27
- else if (result.command === "relay") await runRelay(result);
28
- else if (result.command === "bench") await runBench(result);
7
+ await (await parseCliProgram(process.argv.slice(2))).run();
29
8
  }
30
9
  await main();
31
10
  //#endregion
package/dist/nodeinfo.js CHANGED
@@ -1,20 +1,20 @@
1
1
  import "@js-temporal/polyfill";
2
- import { colors, formatObject } from "./utils.js";
3
2
  import { configContext } from "./config.js";
4
3
  import { userAgentOption } from "./options.js";
4
+ import { colors, formatObject } from "./utils.js";
5
5
  import process from "node:process";
6
- import { getNodeInfo } from "@fedify/fedify";
7
- import { getUserAgent } from "@fedify/vocab-runtime";
8
6
  import { argument, command, constant, flag, group, merge, message, object, string, text } from "@optique/core";
9
7
  import { print, printError } from "@optique/run";
10
- import { getLogger } from "@logtape/logtape";
11
8
  import os from "node:os";
12
9
  import { bindConfig } from "@optique/config";
13
- import ora from "ora";
10
+ import { getUserAgent } from "@fedify/vocab-runtime";
11
+ import { getNodeInfo } from "@fedify/fedify";
14
12
  import { createJimp } from "@jimp/core";
15
13
  import webp from "@jimp/wasm-webp";
14
+ import { getLogger } from "@logtape/logtape";
16
15
  import { isICO, parseICO } from "icojs";
17
16
  import { defaultFormats, defaultPlugins, intToRGBA } from "jimp";
17
+ import ora from "ora";
18
18
  //#region src/nodeinfo.ts
19
19
  const logger = getLogger([
20
20
  "fedify",
@@ -46,15 +46,17 @@ const nodeInfoOption = merge(object("Display options", {
46
46
  key: (config) => config.nodeinfo?.bestEffort ?? false,
47
47
  default: false
48
48
  }) }));
49
- const nodeInfoCommand = command("nodeinfo", merge(object("Arguments", {
49
+ const nodeInfoOptions = merge(object("Arguments", {
50
50
  command: constant("nodeinfo"),
51
51
  host: argument(string({ metavar: "HOST" }), { description: message`Bare hostname or a full URL of the instance` })
52
- }), nodeInfoOption, group("Network options", userAgentOption)), {
52
+ }), nodeInfoOption, group("Network options", userAgentOption));
53
+ const nodeInfoMetadata = {
53
54
  brief: message`Get information about a remote node using the NodeInfo protocol`,
54
55
  description: message`Get information about a remote node using the NodeInfo protocol.
55
56
 
56
57
  The argument is the hostname of the remote node, or the URL of the remote node.`
57
- });
58
+ };
59
+ command("nodeinfo", nodeInfoOptions, nodeInfoMetadata);
58
60
  async function runNodeInfo(command) {
59
61
  const spinner = ora({
60
62
  text: "Fetching a NodeInfo document...",
@@ -270,4 +272,4 @@ function getAsciiArt(image, width = DEFAULT_IMAGE_WIDTH, colorSupport, colors) {
270
272
  return art;
271
273
  }
272
274
  //#endregion
273
- export { Jimp, nodeInfoCommand, runNodeInfo };
275
+ export { Jimp, nodeInfoMetadata, nodeInfoOptions, runNodeInfo };