@fedify/cli 2.3.0-dev.1219 → 2.3.0-dev.1258

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 (61) hide show
  1. package/dist/bench/action.js +203 -0
  2. package/dist/bench/actor/documents.js +39 -0
  3. package/dist/bench/actor/fleet.js +39 -0
  4. package/dist/bench/actor/keys.js +35 -0
  5. package/dist/bench/command.js +42 -0
  6. package/dist/bench/discovery/discover.js +67 -0
  7. package/dist/bench/discovery/probe.js +50 -0
  8. package/dist/bench/load/arrival.js +27 -0
  9. package/dist/bench/load/clock.js +15 -0
  10. package/dist/bench/load/generator.js +112 -0
  11. package/dist/bench/metrics/aggregate.js +64 -0
  12. package/dist/bench/metrics/histogram.js +141 -0
  13. package/dist/bench/metrics/stats-client.js +154 -0
  14. package/dist/bench/mod.js +4 -0
  15. package/dist/bench/render/format.js +46 -0
  16. package/dist/bench/render/index.js +20 -0
  17. package/dist/bench/render/json.js +12 -0
  18. package/dist/bench/render/markdown.js +62 -0
  19. package/dist/bench/render/text.js +74 -0
  20. package/dist/bench/result/build.js +129 -0
  21. package/dist/bench/result/expect/assert.js +74 -0
  22. package/dist/bench/result/expect/evaluate.js +128 -0
  23. package/dist/bench/result/expect/metrics.js +34 -0
  24. package/dist/bench/result/schema.js +15 -0
  25. package/dist/bench/safety/gate.js +54 -0
  26. package/dist/bench/safety/tiers.js +41 -0
  27. package/dist/bench/scenario/coerce.js +24 -0
  28. package/dist/bench/scenario/errors.js +36 -0
  29. package/dist/bench/scenario/load.js +69 -0
  30. package/dist/bench/scenario/normalize.js +126 -0
  31. package/dist/bench/scenario/schema.js +358 -0
  32. package/dist/bench/scenario/units.js +56 -0
  33. package/dist/bench/scenario/validate.js +29 -0
  34. package/dist/bench/scenarios/inbox.js +155 -0
  35. package/dist/bench/scenarios/registry.js +21 -0
  36. package/dist/bench/scenarios/runner.js +76 -0
  37. package/dist/bench/scenarios/webfinger.js +44 -0
  38. package/dist/bench/server/synthetic.js +118 -0
  39. package/dist/bench/signing/activity-id.js +18 -0
  40. package/dist/bench/signing/pipeline.js +134 -0
  41. package/dist/bench/signing/signer.js +39 -0
  42. package/dist/bench/template/generate.js +90 -0
  43. package/dist/bench/template/helpers.js +19 -0
  44. package/dist/bench/template/template.js +132 -0
  45. package/dist/cache.js +1 -1
  46. package/dist/config.js +14 -2
  47. package/dist/deno.js +1 -1
  48. package/dist/generate-vocab/action.js +3 -3
  49. package/dist/generate-vocab/command.js +1 -1
  50. package/dist/imagerenderer.js +1 -1
  51. package/dist/inbox.js +1 -1
  52. package/dist/lookup.js +34 -34
  53. package/dist/mod.js +3 -0
  54. package/dist/nodeinfo.js +6 -6
  55. package/dist/options.js +1 -1
  56. package/dist/runner.js +9 -8
  57. package/dist/tempserver.js +1 -1
  58. package/dist/tunnel.js +2 -2
  59. package/dist/utils.js +3 -2
  60. package/dist/webfinger/action.js +2 -2
  61. package/package.json +12 -10
@@ -0,0 +1,90 @@
1
+ import "@js-temporal/polyfill";
2
+ //#region src/bench/template/generate.ts
3
+ /**
4
+ * Typed payload-generation directives for the scenario format.
5
+ *
6
+ * Rather than templating payload bodies as strings, the format uses typed
7
+ * directives such as `content: { generate: lorem, size: 2KB }`, which are
8
+ * JSON-Schema-validatable and produce deterministic output of a given byte
9
+ * size.
10
+ * @since 2.3.0
11
+ * @module
12
+ */
13
+ /**
14
+ * The largest payload {@link resolveGenerate} will produce (100 MiB). A
15
+ * generated payload is held in memory as a single string, so a much larger size
16
+ * would exhaust memory or overflow `String.repeat`; a realistic benchmark body
17
+ * is far smaller. (`parseSize` itself stays a plain parser with no limit.)
18
+ */
19
+ const MAX_PAYLOAD_SIZE = 100 * 1024 * 1024;
20
+ /** Multipliers for the size units accepted by {@link parseSize}. */
21
+ const SIZE_UNITS = {
22
+ b: 1,
23
+ kb: 1024,
24
+ kib: 1024,
25
+ mb: 1024 ** 2,
26
+ mib: 1024 ** 2,
27
+ gb: 1024 ** 3,
28
+ gib: 1024 ** 3
29
+ };
30
+ const SIZE_RE = /^\s*(\d+(?:\.\d+)?)\s*(b|kb|kib|mb|mib|gb|gib)?\s*$/i;
31
+ /**
32
+ * Parses a human-friendly byte size such as `"2KB"`, `"1.5MiB"`, or `512` into
33
+ * a number of bytes. Units are binary (`KB` = 1024 bytes); a bare number is
34
+ * interpreted as bytes.
35
+ * @param value A size string or a plain number of bytes.
36
+ * @returns The size in bytes, as a non-negative integer.
37
+ * @throws {RangeError} If the value cannot be parsed or is negative.
38
+ */
39
+ function parseSize(value) {
40
+ if (typeof value === "number") {
41
+ if (!Number.isFinite(value) || value < 0) throw new RangeError(`Invalid size: ${value}.`);
42
+ return ensureSafe(Math.floor(value), value);
43
+ }
44
+ const match = value.match(SIZE_RE);
45
+ if (match == null) throw new RangeError(`Invalid size: ${JSON.stringify(value)}.`);
46
+ const amount = Number.parseFloat(match[1]);
47
+ const unit = (match[2] ?? "b").toLowerCase();
48
+ return ensureSafe(Math.floor(amount * SIZE_UNITS[unit]), value);
49
+ }
50
+ function ensureSafe(bytes, original) {
51
+ if (!Number.isSafeInteger(bytes)) throw new RangeError(`Size out of range: ${JSON.stringify(original)}.`);
52
+ return bytes;
53
+ }
54
+ /**
55
+ * Determines whether a value is a {@link GenerateDirective} rather than a plain
56
+ * literal (such as a string content body).
57
+ * @param value The value to test.
58
+ * @returns `true` if the value is a generate directive.
59
+ */
60
+ function isGenerateDirective(value) {
61
+ return value != null && typeof value === "object" && !Array.isArray(value) && Object.hasOwn(value, "generate") && typeof value.generate === "string";
62
+ }
63
+ /** A fixed lorem ipsum corpus used by the `lorem` generator. */
64
+ const LOREM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ";
65
+ /**
66
+ * Resolves a {@link GenerateDirective} into a deterministic payload string.
67
+ *
68
+ * The output is exactly the requested number of bytes (ASCII, so bytes equal
69
+ * characters) and is identical across calls for the same directive, which keeps
70
+ * benchmark payloads reproducible.
71
+ * @param directive The directive to resolve.
72
+ * @returns The generated payload string.
73
+ * @throws {RangeError} If the generator is unknown or the size is invalid.
74
+ */
75
+ function resolveGenerate(directive) {
76
+ const size = directive.size == null ? 0 : parseSize(directive.size);
77
+ if (size > MAX_PAYLOAD_SIZE) throw new RangeError(`Payload size ${JSON.stringify(directive.size)} exceeds the maximum of ${MAX_PAYLOAD_SIZE} bytes.`);
78
+ switch (directive.generate) {
79
+ case "lorem": return generateLorem(size);
80
+ default: throw new RangeError(`Unknown payload generator: ${JSON.stringify(directive.generate)}.`);
81
+ }
82
+ }
83
+ function generateLorem(size) {
84
+ if (size <= 0) return "";
85
+ let out = LOREM.repeat(Math.ceil(size / 335));
86
+ if (out.length > size) out = out.slice(0, size);
87
+ return out;
88
+ }
89
+ //#endregion
90
+ export { isGenerateDirective, resolveGenerate };
@@ -0,0 +1,19 @@
1
+ import "@js-temporal/polyfill";
2
+ //#region src/bench/template/helpers.ts
3
+ /**
4
+ * Returns a fresh registry of the default template helpers:
5
+ *
6
+ * - `uuid()` — a random UUID string.
7
+ * - `upper(value)` — the uppercase form of the argument.
8
+ * - `lower(value)` — the lowercase form of the argument.
9
+ * @returns A new record of helper functions.
10
+ */
11
+ function defaultHelpers() {
12
+ return {
13
+ uuid: () => crypto.randomUUID(),
14
+ upper: (value) => String(value).toUpperCase(),
15
+ lower: (value) => String(value).toLowerCase()
16
+ };
17
+ }
18
+ //#endregion
19
+ export { defaultHelpers };
@@ -0,0 +1,132 @@
1
+ import "@js-temporal/polyfill";
2
+ //#region src/bench/template/template.ts
3
+ /** An error raised while rendering a `${{ ... }}` template expression. */
4
+ var TemplateError = class extends Error {};
5
+ const EXPR_RE = /\$\{\{([\s\S]*?)\}\}/g;
6
+ const CALL_RE = /^([A-Za-z_]\w*)\s*\(([\s\S]*)\)$/;
7
+ const IDENT_RE = /^[A-Za-z_]\w*$/;
8
+ /** Property names that must never be resolved, to avoid prototype access. */
9
+ const FORBIDDEN = new Set([
10
+ "__proto__",
11
+ "prototype",
12
+ "constructor"
13
+ ]);
14
+ /** A guard against unbounded recursion on pathologically nested input. */
15
+ const MAX_DEPTH = 100;
16
+ /**
17
+ * Recursively renders every `${{ ... }}` expression in a value.
18
+ *
19
+ * When a string consists of a single expression, the raw evaluated value is
20
+ * returned (so `${{ count }}` can yield a number). When an expression is
21
+ * embedded in surrounding text, its result is stringified and interpolated.
22
+ * Objects and arrays are walked recursively; other scalars pass through.
23
+ * @typeParam T The value type.
24
+ * @param value The value to render.
25
+ * @param context The evaluation context.
26
+ * @returns The rendered value, of the same shape as the input.
27
+ */
28
+ function renderTemplates(value, context = {}) {
29
+ return renderValue(value, context);
30
+ }
31
+ function renderValue(value, ctx, depth = 0) {
32
+ if (depth > MAX_DEPTH) throw new TemplateError("Maximum template nesting depth exceeded.");
33
+ if (typeof value === "string") return renderString(value, ctx);
34
+ if (Array.isArray(value)) {
35
+ let out;
36
+ for (let i = 0; i < value.length; i++) {
37
+ const item = value[i];
38
+ const rendered = renderValue(item, ctx, depth + 1);
39
+ if (out == null && rendered !== item) out = value.slice(0, i);
40
+ if (out != null) out.push(rendered);
41
+ }
42
+ return out ?? value;
43
+ }
44
+ if (value != null && typeof value === "object") {
45
+ const entries = Object.entries(value);
46
+ let out;
47
+ for (let i = 0; i < entries.length; i++) {
48
+ const [key, item] = entries[i];
49
+ const rendered = renderValue(item, ctx, depth + 1);
50
+ if (out == null && rendered !== item) {
51
+ out = {};
52
+ for (let j = 0; j < i; j++) out[entries[j][0]] = entries[j][1];
53
+ }
54
+ if (out != null) out[key] = rendered;
55
+ }
56
+ return out ?? value;
57
+ }
58
+ return value;
59
+ }
60
+ function renderString(str, ctx) {
61
+ const matches = [...str.matchAll(EXPR_RE)];
62
+ if (str.split("${{").length - 1 !== matches.length) throw new TemplateError(`Unclosed \${{ }} expression: ${str}`);
63
+ if (matches.length === 0) return str;
64
+ const only = matches[0];
65
+ if (matches.length === 1 && str.slice(0, only.index).trim() === "" && str.slice(only.index + only[0].length).trim() === "") return evalExpr(only[1], ctx);
66
+ return str.replace(EXPR_RE, (_match, expr) => stringify(evalExpr(expr, ctx)));
67
+ }
68
+ function evalExpr(source, ctx) {
69
+ const expr = source.trim();
70
+ if (expr === "") throw new TemplateError("Empty ${{ }} expression.");
71
+ const call = expr.match(CALL_RE);
72
+ if (call != null) {
73
+ const name = call[1];
74
+ const helper = FORBIDDEN.has(name) || ctx.helpers == null || !Object.hasOwn(ctx.helpers, name) ? void 0 : ctx.helpers[name];
75
+ if (typeof helper !== "function") throw new TemplateError(`Unknown helper: ${name}.`);
76
+ return helper(...parseArgs(call[2], ctx));
77
+ }
78
+ return resolvePath(expr, ctx.values ?? {});
79
+ }
80
+ function parseArgs(source, ctx) {
81
+ const trimmed = source.trim();
82
+ if (trimmed === "") return [];
83
+ return splitTopLevel(trimmed).map((arg) => parseArg(arg.trim(), ctx));
84
+ }
85
+ function splitTopLevel(source) {
86
+ const parts = [];
87
+ let current = "";
88
+ let quote = null;
89
+ let escaped = false;
90
+ for (const char of source) if (escaped) {
91
+ current += char;
92
+ escaped = false;
93
+ } else if (char === "\\") {
94
+ current += char;
95
+ escaped = true;
96
+ } else if (quote != null) {
97
+ if (char === quote) quote = null;
98
+ current += char;
99
+ } else if (char === "'" || char === "\"") {
100
+ quote = char;
101
+ current += char;
102
+ } else if (char === ",") {
103
+ parts.push(current);
104
+ current = "";
105
+ } else current += char;
106
+ if (quote != null) throw new TemplateError("Unbalanced quote in helper arguments.");
107
+ parts.push(current);
108
+ return parts;
109
+ }
110
+ function parseArg(arg, ctx) {
111
+ const str = arg.match(/^'([\s\S]*)'$/) ?? arg.match(/^"([\s\S]*)"$/);
112
+ if (str != null) return str[1].replace(/\\(.)/g, "$1");
113
+ if (/^-?\d+(?:\.\d+)?$/.test(arg)) return Number(arg);
114
+ if (arg === "true") return true;
115
+ if (arg === "false") return false;
116
+ if (arg === "null") return null;
117
+ return resolvePath(arg, ctx.values ?? {});
118
+ }
119
+ function resolvePath(path, values) {
120
+ let current = values;
121
+ for (const part of path.split(".")) {
122
+ if (!IDENT_RE.test(part) || FORBIDDEN.has(part)) throw new TemplateError(`Invalid reference: ${path}.`);
123
+ if (current == null || typeof current !== "object" || !Object.hasOwn(current, part)) throw new TemplateError(`Unknown reference: ${path}.`);
124
+ current = current[part];
125
+ }
126
+ return current;
127
+ }
128
+ function stringify(value) {
129
+ return value == null ? "" : String(value);
130
+ }
131
+ //#endregion
132
+ export { renderTemplates };
package/dist/cache.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import { mkdir } from "node:fs/promises";
3
3
  import process from "node:process";
4
- import { join } from "node:path";
5
4
  import { homedir } from "node:os";
5
+ import { join } from "node:path";
6
6
  //#region src/cache.ts
7
7
  /**
8
8
  * Returns the default cache directory path.
package/dist/config.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import "@js-temporal/polyfill";
2
- import { printError } from "@optique/run";
3
2
  import { message } from "@optique/core";
4
3
  import { createConfigContext } from "@optique/config";
4
+ import { printError } from "@optique/run";
5
5
  import { readFileSync } from "node:fs";
6
6
  import { parse } from "smol-toml";
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";
@@ -76,6 +76,17 @@ const nodeinfoSchema = object$1({
76
76
  showMetadata: optional$1(boolean())
77
77
  });
78
78
  /**
79
+ * Schema for the bench command configuration.
80
+ *
81
+ * `allowUnsafeTarget` is intentionally absent: the unsafe-target override is a
82
+ * CLI-only, per-run acknowledgment, never a persisted default.
83
+ */
84
+ const benchSchema = object$1({ format: optional$1(picklist([
85
+ "text",
86
+ "json",
87
+ "markdown"
88
+ ])) });
89
+ /**
79
90
  * Config context for use with bindConfig().
80
91
  */
81
92
  const configContext = createConfigContext({ schema: object$1({
@@ -90,7 +101,8 @@ const configContext = createConfigContext({ schema: object$1({
90
101
  lookup: optional$1(lookupSchema),
91
102
  inbox: optional$1(inboxSchema),
92
103
  relay: optional$1(relaySchema),
93
- nodeinfo: optional$1(nodeinfoSchema)
104
+ nodeinfo: optional$1(nodeinfoSchema),
105
+ bench: optional$1(benchSchema)
94
106
  }) });
95
107
  /**
96
108
  * Try to load and parse a TOML config file.
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.1219+e9ad1fa3";
3
+ var version = "2.3.0-dev.1258+a2a67992";
4
4
  //#endregion
5
5
  export { version };
@@ -1,9 +1,9 @@
1
1
  import "@js-temporal/polyfill";
2
- import { generateVocab } from "@fedify/vocab-tools";
3
- import { message } from "@optique/core/message";
4
- import { printError } from "@optique/run";
5
2
  import { stat } from "node:fs/promises";
6
3
  import process from "node:process";
4
+ import { printError } from "@optique/run";
5
+ import { generateVocab } from "@fedify/vocab-tools";
6
+ import { message } from "@optique/core/message";
7
7
  //#region src/generate-vocab/action.ts
8
8
  async function runGenerateVocab({ schemaDir, generatedPath }) {
9
9
  if (!(await stat(schemaDir)).isDirectory()) {
@@ -1,6 +1,6 @@
1
1
  import "@js-temporal/polyfill";
2
- import { path } from "@optique/run";
3
2
  import { argument, command, constant, message, object, option, withDefault } from "@optique/core";
3
+ import { path } from "@optique/run";
4
4
  //#region src/generate-vocab/command.ts
5
5
  const schemaDir = withDefault(option("-i", "--input", path({
6
6
  metavar: "DIR",
@@ -3,9 +3,9 @@ import { Jimp } from "./nodeinfo.js";
3
3
  import fs from "node:fs/promises";
4
4
  import process from "node:process";
5
5
  import { validatePublicUrl } from "@fedify/vocab-runtime";
6
+ import os from "node:os";
6
7
  import path from "node:path";
7
8
  import { encodeBase64 } from "byte-encodings/base64";
8
- import os from "node:os";
9
9
  //#region src/imagerenderer.ts
10
10
  const KITTY_IDENTIFIERS = [
11
11
  "kitty",
package/dist/inbox.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
- import { version } from "./deno.js";
3
2
  import { getDocumentLoader } from "./docloader.js";
3
+ import { version } from "./deno.js";
4
4
  import { ActivityEntryPage, ActivityListPage } from "./inbox/view.js";
5
5
  import { configureLogging, recordingSink } from "./log.js";
6
6
  import { tableStyle } from "./table.js";
package/dist/lookup.js CHANGED
@@ -1,22 +1,22 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import { getContextLoader, getDocumentLoader as getDocumentLoader$1 } from "./docloader.js";
3
+ import { configContext } from "./config.js";
4
+ import { createTunnelServiceOption, userAgentOption } from "./options.js";
3
5
  import { configureLogging } from "./log.js";
4
6
  import { spawnTemporaryServer } from "./tempserver.js";
5
7
  import { colorEnabled, colors, formatObject } from "./utils.js";
6
- import { configContext } from "./config.js";
7
- import { createTunnelServiceOption, userAgentOption } from "./options.js";
8
8
  import { renderImages } from "./imagerenderer.js";
9
- import { url } from "@optique/core/message";
10
- import { path, printError } from "@optique/run";
11
9
  import process from "node:process";
12
- import { argument, choice, command, constant, flag, float, integer, map, merge, message as message$1, multiple, object, option, optionNames, optional, or, string, withDefault } from "@optique/core";
13
10
  import { generateCryptoKeyPair, getAuthenticatedDocumentLoader, respondWithObject } from "@fedify/fedify";
11
+ import { UrlError, expandIPv6Address, isValidPublicIPv4Address, isValidPublicIPv6Address } from "@fedify/vocab-runtime";
14
12
  import { Application, Collection, CryptographicKey, Object as Object$1, lookupObject, traverseCollection } from "@fedify/vocab";
15
13
  import { getLogger } from "@logtape/logtape";
16
- import ora from "ora";
17
- import { UrlError, expandIPv6Address, isValidPublicIPv4Address, isValidPublicIPv6Address } from "@fedify/vocab-runtime";
14
+ import { argument, choice, command, constant, flag, float, integer, map, merge, message, multiple, object, option, optionNames, optional, or, string, withDefault } from "@optique/core";
18
15
  import { bindConfig } from "@optique/config";
16
+ import { path, printError } from "@optique/run";
19
17
  import { createWriteStream } from "node:fs";
18
+ import { url } from "@optique/core/message";
19
+ import ora from "ora";
20
20
  import { isIP } from "node:net";
21
21
  //#region src/lookup.ts
22
22
  const logger = getLogger([
@@ -39,12 +39,12 @@ const recurseProperties = [
39
39
  MISSKEY_QUOTE_IRI,
40
40
  FEDIBIRD_QUOTE_IRI
41
41
  ];
42
- const suppressErrorsOption = bindConfig(flag("-S", "--suppress-errors", { description: message$1`Suppress partial errors during traversal or recursion.` }), {
42
+ const suppressErrorsOption = bindConfig(flag("-S", "--suppress-errors", { description: message`Suppress partial errors during traversal or recursion.` }), {
43
43
  context: configContext,
44
44
  key: (config) => config.lookup?.suppressErrors ?? false,
45
45
  default: false
46
46
  });
47
- const allowPrivateAddressOption = bindConfig(flag("-p", "--allow-private-address", { description: message$1`Allow private IP addresses for URLs discovered \
47
+ const allowPrivateAddressOption = bindConfig(flag("-p", "--allow-private-address", { description: message`Allow private IP addresses for URLs discovered \
48
48
  during traversal or recursive object fetches. Recursive JSON-LD \
49
49
  context URLs always remain blocked. URLs explicitly provided on the \
50
50
  command line always allow private addresses.` }), {
@@ -53,11 +53,11 @@ command line always allow private addresses.` }), {
53
53
  default: false
54
54
  });
55
55
  const authorizedFetchOption = withDefault(object("Authorized fetch options", {
56
- authorizedFetch: bindConfig(map(flag("-a", "--authorized-fetch", { description: message$1`Sign the request with an one-time key.` }), () => true), {
56
+ authorizedFetch: bindConfig(map(flag("-a", "--authorized-fetch", { description: message`Sign the request with an one-time key.` }), () => true), {
57
57
  context: configContext,
58
58
  key: (config) => config.lookup?.authorizedFetch ? true : void 0
59
59
  }),
60
- firstKnock: bindConfig(option("--first-knock", choice(["draft-cavage-http-signatures-12", "rfc9421"]), { description: message$1`The first-knock spec for ${optionNames(["-a", "--authorized-fetch"])}. It is used for the double-knocking technique.` }), {
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
61
  context: configContext,
62
62
  key: (config) => config.lookup?.firstKnock ?? "draft-cavage-http-signatures-12",
63
63
  default: "draft-cavage-http-signatures-12"
@@ -70,21 +70,21 @@ const authorizedFetchOption = withDefault(object("Authorized fetch options", {
70
70
  });
71
71
  const lookupModeOption = withDefault(or(object("Recurse options", {
72
72
  traverse: constant(false),
73
- recurse: bindConfig(option("--recurse", choice(recurseProperties, { metavar: "PROPERTY" }), { description: message$1`Recursively follow a relationship property.` }), {
73
+ recurse: bindConfig(option("--recurse", choice(recurseProperties, { metavar: "PROPERTY" }), { description: message`Recursively follow a relationship property.` }), {
74
74
  context: configContext,
75
75
  key: (config) => config.lookup?.recurse
76
76
  }),
77
77
  recurseDepth: bindConfig(option("--recurse-depth", integer({
78
78
  min: 1,
79
79
  metavar: "DEPTH"
80
- }), { description: message$1`Maximum recursion depth for ${optionNames(["--recurse"])}.` }), {
80
+ }), { description: message`Maximum recursion depth for ${optionNames(["--recurse"])}.` }), {
81
81
  context: configContext,
82
82
  key: (config) => config.lookup?.recurseDepth,
83
83
  default: 20
84
84
  }),
85
85
  suppressErrors: suppressErrorsOption
86
86
  }), object("Traverse options", {
87
- traverse: bindConfig(flag("-t", "--traverse", { description: message$1`Traverse the given collection(s) to fetch all items.` }), {
87
+ traverse: bindConfig(flag("-t", "--traverse", { description: message`Traverse the given collection(s) to fetch all items.` }), {
88
88
  context: configContext,
89
89
  key: (config) => config.lookup?.traverse ?? false,
90
90
  default: false
@@ -103,22 +103,22 @@ const lookupCommand = command("lookup", merge(object({ command: constant("lookup
103
103
  timeout: optional(bindConfig(option("-T", "--timeout", float({
104
104
  min: 0,
105
105
  metavar: "SECONDS"
106
- }), { description: message$1`Set timeout for network requests in seconds.` }), {
106
+ }), { description: message`Set timeout for network requests in seconds.` }), {
107
107
  context: configContext,
108
108
  key: (config) => config.lookup?.timeout
109
109
  }))
110
- })), object("Arguments", { urls: multiple(argument(string({ metavar: "URL_OR_HANDLE" }), { description: message$1`One or more URLs or handles to look up.` }), { min: 1 }) }), object("Output options", {
111
- reverse: bindConfig(flag("--reverse", { description: message$1`Reverse the output order of fetched objects or items.` }), {
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
112
  context: configContext,
113
113
  key: (config) => config.lookup?.reverse ?? false,
114
114
  default: false
115
115
  }),
116
- format: bindConfig(optional(or(map(flag("-r", "--raw", { description: message$1`Print the fetched JSON-LD document as is.` }), () => "raw"), map(flag("-C", "--compact", { description: message$1`Compact the fetched JSON-LD document.` }), () => "compact"), map(flag("-e", "--expand", { description: message$1`Expand the fetched JSON-LD document.` }), () => "expand"))), {
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
117
  context: configContext,
118
118
  key: (config) => config.lookup?.defaultFormat ?? "default",
119
119
  default: "default"
120
120
  }),
121
- separator: bindConfig(option("-s", "--separator", string({ metavar: "SEPARATOR" }), { description: message$1`Specify the separator between adjacent output objects or collection items.` }), {
121
+ separator: bindConfig(option("-s", "--separator", string({ metavar: "SEPARATOR" }), { description: message`Specify the separator between adjacent output objects or collection items.` }), {
122
122
  context: configContext,
123
123
  key: (config) => config.lookup?.separator ?? "----",
124
124
  default: "----"
@@ -127,10 +127,10 @@ const lookupCommand = command("lookup", merge(object({ command: constant("lookup
127
127
  metavar: "OUTPUT_PATH",
128
128
  type: "file",
129
129
  allowCreate: true
130
- }), { description: message$1`Specify the output file path.` }))
130
+ }), { description: message`Specify the output file path.` }))
131
131
  })), {
132
- brief: message$1`Look up Activity Streams objects.`,
133
- description: message$1`Look up Activity Streams objects by URL or actor handle.
132
+ brief: message`Look up Activity Streams objects.`,
133
+ description: message`Look up Activity Streams objects by URL or actor handle.
134
134
 
135
135
  The arguments can be either URLs or actor handles (e.g., ${"@username@domain"}), and they can be multiple.`
136
136
  });
@@ -282,7 +282,7 @@ function wrapDocumentLoaderWithTimeout(loader, timeoutSeconds) {
282
282
  function handleTimeoutError(spinner, timeoutSeconds, url) {
283
283
  const urlText = url ? ` for: ${colors.red(url)}` : "";
284
284
  spinner.fail(`Request timed out after ${timeoutSeconds} seconds${urlText}.`);
285
- printError(message$1`Try increasing the timeout with ${optionNames(["-T", "--timeout"])} option or check network connectivity.`);
285
+ printError(message`Try increasing the timeout with ${optionNames(["-T", "--timeout"])} option or check network connectivity.`);
286
286
  }
287
287
  function isPrivateAddressError(error) {
288
288
  const lowerMessage = (error instanceof Error ? error.message : String(error)).toLowerCase();
@@ -318,10 +318,10 @@ function getPrivateContextUrl(error) {
318
318
  return getPrivateUrlCandidate(match[1]);
319
319
  }
320
320
  function printRecursivePrivateAddressHint() {
321
- printError(message$1`The recursive target appears to be private or localhost. Try with ${optionNames(["-p", "--allow-private-address"])}, or use ${optionNames(["-S", "--suppress-errors"])} to skip blocked steps.`);
321
+ printError(message`The recursive target appears to be private or localhost. Try with ${optionNames(["-p", "--allow-private-address"])}, or use ${optionNames(["-S", "--suppress-errors"])} to skip blocked steps.`);
322
322
  }
323
323
  function printRecursivePrivateContextHint(privateContextUrl) {
324
- printError(message$1`Recursive JSON-LD context URL ${url(privateContextUrl)} is always blocked, even with ${optionNames(["-p", "--allow-private-address"])}. Use ${optionNames(["-S", "--suppress-errors"])} to skip blocked steps.`);
324
+ printError(message`Recursive JSON-LD context URL ${url(privateContextUrl)} is always blocked, even with ${optionNames(["-p", "--allow-private-address"])}. Use ${optionNames(["-S", "--suppress-errors"])} to skip blocked steps.`);
325
325
  }
326
326
  function getLookupFailureHint(error, options = {}) {
327
327
  if (isPrivateAddressError(error)) return options.recursive ? "recursive-private-address" : "private-address";
@@ -338,13 +338,13 @@ function printLookupFailureHint(authLoader, error, options = {}) {
338
338
  if (!shouldPrintLookupFailureHint(authLoader, hint)) return;
339
339
  switch (hint) {
340
340
  case "private-address":
341
- printError(message$1`The URL appears to be private or localhost. Try with ${optionNames(["-p", "--allow-private-address"])}.`);
341
+ printError(message`The URL appears to be private or localhost. Try with ${optionNames(["-p", "--allow-private-address"])}.`);
342
342
  return;
343
343
  case "recursive-private-address":
344
344
  printRecursivePrivateAddressHint();
345
345
  return;
346
346
  case "authorized-fetch":
347
- printError(message$1`It may be a private object. Try with ${optionNames(["-a", "--authorized-fetch"])}.`);
347
+ printError(message`It may be a private object. Try with ${optionNames(["-a", "--authorized-fetch"])}.`);
348
348
  return;
349
349
  }
350
350
  }
@@ -418,7 +418,7 @@ async function runLookup(command, deps = {}) {
418
418
  ...deps
419
419
  };
420
420
  if (command.urls.length < 1) {
421
- printError(message$1`At least one URL or actor handle must be provided.`);
421
+ printError(message`At least one URL or actor handle must be provided.`);
422
422
  effectiveDeps.exit(1);
423
423
  }
424
424
  if (command.debug) await configureLogging();
@@ -559,7 +559,7 @@ async function runLookup(command, deps = {}) {
559
559
  }
560
560
  if (current == null) {
561
561
  spinner.fail(`Failed to fetch object: ${colors.red(url)}.`);
562
- if (authLoader == null) printError(message$1`It may be a private object. Try with ${optionNames(["-a", "--authorized-fetch"])}.`);
562
+ if (authLoader == null) printError(message`It may be a private object. Try with ${optionNames(["-a", "--authorized-fetch"])}.`);
563
563
  await finalizeAndExit(1);
564
564
  return;
565
565
  }
@@ -601,7 +601,7 @@ async function runLookup(command, deps = {}) {
601
601
  else if (error instanceof RecursiveLookupError) {
602
602
  spinner.fail(`Failed to recursively fetch object: ${colors.red(error.target)}.`);
603
603
  if (!command.allowPrivateAddress && isPrivateAddressTarget(error.target)) printRecursivePrivateAddressHint();
604
- else if (authLoader == null) printError(message$1`It may be a private object. Try with ${optionNames(["-a", "--authorized-fetch"])}.`);
604
+ else if (authLoader == null) printError(message`It may be a private object. Try with ${optionNames(["-a", "--authorized-fetch"])}.`);
605
605
  } else {
606
606
  spinner.fail("Failed to recursively fetch object.");
607
607
  const privateContextUrl = getPrivateContextUrl(error);
@@ -611,7 +611,7 @@ async function runLookup(command, deps = {}) {
611
611
  return;
612
612
  }
613
613
  const hint = getLookupFailureHint(error, { recursive: true });
614
- if (shouldSuggestSuppressErrorsForLookupFailure(authLoader, hint)) printError(message$1`Use the ${optionNames(["-S", "--suppress-errors"])} option to suppress partial errors.`);
614
+ if (shouldSuggestSuppressErrorsForLookupFailure(authLoader, hint)) printError(message`Use the ${optionNames(["-S", "--suppress-errors"])} option to suppress partial errors.`);
615
615
  else printLookupFailureHint(authLoader, error, { recursive: true });
616
616
  }
617
617
  await finalizeAndExit(1);
@@ -685,7 +685,7 @@ async function runLookup(command, deps = {}) {
685
685
  }
686
686
  if (collection == null) {
687
687
  spinner.fail(`Failed to fetch object: ${colors.red(url)}.`);
688
- if (authLoader == null) printError(message$1`It may be a private object. Try with ${optionNames(["-a", "--authorized-fetch"])}.`);
688
+ if (authLoader == null) printError(message`It may be a private object. Try with ${optionNames(["-a", "--authorized-fetch"])}.`);
689
689
  await finalizeAndExit(1);
690
690
  return;
691
691
  }
@@ -747,7 +747,7 @@ async function runLookup(command, deps = {}) {
747
747
  else {
748
748
  spinner.fail(`Failed to complete the traversal for: ${colors.red(url)}.`);
749
749
  const hint = getLookupFailureHint(error);
750
- if (shouldSuggestSuppressErrorsForLookupFailure(authLoader, hint)) printError(message$1`Use the ${optionNames(["-S", "--suppress-errors"])} option to suppress partial errors.`);
750
+ if (shouldSuggestSuppressErrorsForLookupFailure(authLoader, hint)) printError(message`Use the ${optionNames(["-S", "--suppress-errors"])} option to suppress partial errors.`);
751
751
  else printLookupFailureHint(authLoader, error);
752
752
  }
753
753
  await finalizeAndExit(1);
@@ -782,7 +782,7 @@ async function runLookup(command, deps = {}) {
782
782
  const url = command.urls[i];
783
783
  if (obj == null) {
784
784
  spinner.fail(`Failed to fetch ${colors.red(url)}`);
785
- if (authLoader == null) printError(message$1`It may be a private object. Try with ${optionNames(["-a", "--authorized-fetch"])}.`);
785
+ if (authLoader == null) printError(message`It may be a private object. Try with ${optionNames(["-a", "--authorized-fetch"])}.`);
786
786
  success = false;
787
787
  } else {
788
788
  spinner.succeed(`Fetched object: ${colors.green(url)}`);
package/dist/mod.js CHANGED
@@ -1,5 +1,7 @@
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";
3
5
  import runGenerateVocab from "./generate-vocab/action.js";
4
6
  import "./generate-vocab/mod.js";
5
7
  import { runInbox } from "./inbox.js";
@@ -23,6 +25,7 @@ async function main() {
23
25
  else if (result.command === "tunnel") await runTunnel(result);
24
26
  else if (result.command === "generate-vocab") await runGenerateVocab(result);
25
27
  else if (result.command === "relay") await runRelay(result);
28
+ else if (result.command === "bench") await runBench(result);
26
29
  }
27
30
  await main();
28
31
  //#endregion
package/dist/nodeinfo.js CHANGED
@@ -1,16 +1,16 @@
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";
5
- import { print, printError } from "@optique/run";
4
+ import { colors, formatObject } from "./utils.js";
6
5
  import process from "node:process";
7
- import { argument, command, constant, flag, group, merge, message, object, string, text } from "@optique/core";
8
6
  import { getNodeInfo } from "@fedify/fedify";
9
- import { getLogger } from "@logtape/logtape";
10
- import ora from "ora";
11
7
  import { getUserAgent } from "@fedify/vocab-runtime";
12
- import { bindConfig } from "@optique/config";
13
8
  import os from "node:os";
9
+ import { getLogger } from "@logtape/logtape";
10
+ import { argument, command, constant, flag, group, merge, message, object, string, text } from "@optique/core";
11
+ import { bindConfig } from "@optique/config";
12
+ import { print, printError } from "@optique/run";
13
+ import ora from "ora";
14
14
  import { createJimp } from "@jimp/core";
15
15
  import webp from "@jimp/wasm-webp";
16
16
  import { isICO, parseICO } from "icojs";
package/dist/options.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import { configContext } from "./config.js";
3
- import { choice, constant, flag, map, merge, message, object, option, optional, or, string, withDefault } from "@optique/core";
4
3
  import { getUserAgent } from "@fedify/vocab-runtime";
4
+ import { choice, constant, flag, map, merge, message, object, option, optional, or, string, withDefault } from "@optique/core";
5
5
  import { bindConfig } from "@optique/config";
6
6
  //#region src/options.ts
7
7
  /**