@fedify/cli 2.3.0-dev.994 → 2.4.0-dev.1417
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/bench/action.js +469 -0
- package/dist/bench/actor/documents.js +39 -0
- package/dist/bench/actor/fleet.js +39 -0
- package/dist/bench/actor/keys.js +35 -0
- package/dist/bench/command.js +72 -0
- package/dist/bench/compare/schema.js +16 -0
- package/dist/bench/compare.js +667 -0
- package/dist/bench/discovery/discover.js +67 -0
- package/dist/bench/discovery/probe.js +50 -0
- package/dist/bench/load/arrival.js +27 -0
- package/dist/bench/load/clock.js +33 -0
- package/dist/bench/load/generator.js +145 -0
- package/dist/bench/metrics/aggregate.js +64 -0
- package/dist/bench/metrics/histogram.js +141 -0
- package/dist/bench/metrics/stats-client.js +216 -0
- package/dist/bench/mod.js +11 -0
- package/dist/bench/render/format.js +46 -0
- package/dist/bench/render/index.js +20 -0
- package/dist/bench/render/json.js +12 -0
- package/dist/bench/render/markdown.js +63 -0
- package/dist/bench/render/text.js +75 -0
- package/dist/bench/result/build.js +252 -0
- package/dist/bench/result/expect/assert.js +74 -0
- package/dist/bench/result/expect/evaluate.js +128 -0
- package/dist/bench/result/expect/metrics.js +34 -0
- package/dist/bench/result/schema.js +365 -0
- package/dist/bench/safety/gate.js +62 -0
- package/dist/bench/safety/tiers.js +97 -0
- package/dist/bench/scenario/coerce.js +24 -0
- package/dist/bench/scenario/errors.js +36 -0
- package/dist/bench/scenario/load.js +69 -0
- package/dist/bench/scenario/normalize.js +125 -0
- package/dist/bench/scenario/schema.js +399 -0
- package/dist/bench/scenario/units.js +56 -0
- package/dist/bench/scenario/validate.js +29 -0
- package/dist/bench/scenarios/actor.js +38 -0
- package/dist/bench/scenarios/failure.js +363 -0
- package/dist/bench/scenarios/fanout.js +261 -0
- package/dist/bench/scenarios/inbox.js +147 -0
- package/dist/bench/scenarios/mixed.js +244 -0
- package/dist/bench/scenarios/object-discovery.js +211 -0
- package/dist/bench/scenarios/object.js +54 -0
- package/dist/bench/scenarios/read.js +108 -0
- package/dist/bench/scenarios/registry.js +39 -0
- package/dist/bench/scenarios/runner.js +96 -0
- package/dist/bench/scenarios/webfinger.js +44 -0
- package/dist/bench/server/synthetic.js +118 -0
- package/dist/bench/signing/activity-id.js +18 -0
- package/dist/bench/signing/pipeline.js +134 -0
- package/dist/bench/signing/signer.js +39 -0
- package/dist/bench/template/generate.js +90 -0
- package/dist/bench/template/helpers.js +19 -0
- package/dist/bench/template/template.js +132 -0
- package/dist/cache.js +2 -2
- package/dist/commands.js +110 -0
- package/dist/config.js +15 -3
- package/dist/deno.js +1 -1
- package/dist/docloader.js +1 -1
- package/dist/generate-vocab/action.js +3 -3
- package/dist/generate-vocab/command.js +6 -4
- package/dist/imagerenderer.js +3 -3
- package/dist/inbox/command.js +6 -4
- package/dist/inbox/view.js +1 -1
- package/dist/inbox.js +4 -4
- package/dist/log.js +2 -2
- package/dist/lookup/command.js +121 -0
- package/dist/lookup.js +27 -138
- package/dist/mod.js +2 -20
- package/dist/nodeinfo.js +53 -12
- package/dist/options.js +1 -1
- package/dist/relay/command.js +6 -4
- package/dist/relay.js +3 -3
- package/dist/runner.js +70 -45
- package/dist/tunnel.js +8 -6
- package/dist/utils.js +9 -4
- package/dist/webfinger/action.js +1 -1
- package/dist/webfinger/command.js +6 -4
- package/dist/webfinger/error.js +2 -0
- package/dist/webfinger/lib.js +1 -1
- package/package.json +28 -23
- package/dist/generate-vocab/mod.js +0 -4
- package/dist/init/mod.js +0 -3
- package/dist/webfinger/mod.js +0 -4
|
@@ -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
|
-
import { mkdir } from "node:fs/promises";
|
|
3
2
|
import process from "node:process";
|
|
4
|
-
import { join } from "node:path";
|
|
5
3
|
import { homedir } from "node:os";
|
|
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.
|
package/dist/commands.js
ADDED
|
@@ -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
|
-
import { printError } from "@optique/run";
|
|
3
2
|
import { message } from "@optique/core";
|
|
4
|
-
import {
|
|
3
|
+
import { printError } from "@optique/run";
|
|
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
|
/**
|
|
@@ -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
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,9 +1,9 @@
|
|
|
1
1
|
import "@js-temporal/polyfill";
|
|
2
|
-
import
|
|
3
|
-
import { message } from "@optique/core/message";
|
|
2
|
+
import process from "node:process";
|
|
4
3
|
import { printError } from "@optique/run";
|
|
5
4
|
import { stat } from "node:fs/promises";
|
|
6
|
-
import
|
|
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",
|
|
@@ -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
|
|
15
|
+
const generateVocabOptions = object("Generation options", {
|
|
16
16
|
command: constant("generate-vocab"),
|
|
17
17
|
schemaDir,
|
|
18
18
|
generatedPath
|
|
19
|
-
})
|
|
19
|
+
});
|
|
20
|
+
const generateVocabMetadata = { description: message`Generate vocabulary classes from schema files.` };
|
|
21
|
+
command("generate-vocab", generateVocabOptions, generateVocabMetadata);
|
|
20
22
|
//#endregion
|
|
21
|
-
export {
|
|
23
|
+
export { generateVocabMetadata, generateVocabOptions };
|
package/dist/imagerenderer.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
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
|
|
4
|
+
import os from "node:os";
|
|
6
5
|
import path from "node:path";
|
|
6
|
+
import { validatePublicUrl } from "@fedify/vocab-runtime";
|
|
7
|
+
import fs from "node:fs/promises";
|
|
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/command.js
CHANGED
|
@@ -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
|
|
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 {
|
|
43
|
+
export { inboxMetadata, inboxOptions };
|
package/dist/inbox/view.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
import { renderActivity, renderRawActivity, renderRequest, renderResponse } from "./rendercode.js";
|
|
3
|
+
import util from "node:util";
|
|
3
4
|
import { getStatusText } from "@poppanator/http-constants";
|
|
4
5
|
import { Fragment } from "hono/jsx";
|
|
5
6
|
import { getSingletonHighlighter } from "shiki";
|
|
6
|
-
import util from "node:util";
|
|
7
7
|
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
8
8
|
//#region src/inbox/view.tsx
|
|
9
9
|
const Layout = (props) => {
|
package/dist/inbox.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
|
+
import { colors, matchesActor } from "./utils.js";
|
|
3
|
+
import { configureLogging, recordingSink } from "./log.js";
|
|
2
4
|
import { version } from "./deno.js";
|
|
3
5
|
import { getDocumentLoader } from "./docloader.js";
|
|
4
6
|
import { ActivityEntryPage, ActivityListPage } from "./inbox/view.js";
|
|
5
|
-
import { configureLogging, recordingSink } from "./log.js";
|
|
6
7
|
import { tableStyle } from "./table.js";
|
|
7
8
|
import { spawnTemporaryServer } from "./tempserver.js";
|
|
8
|
-
import { colors, matchesActor } from "./utils.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 };
|