Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,10 @@
"@opentelemetry/sdk-metrics": "npm:@opentelemetry/sdk-metrics@2.7.1",
"@opentelemetry/sdk-trace-base": "npm:@opentelemetry/sdk-trace-base@^2.7.1",
"@opentelemetry/semantic-conventions": "npm:@opentelemetry/semantic-conventions@^1.40.0",
"@optique/config": "jsr:@optique/config@^1.0.2",
"@optique/core": "jsr:@optique/core@^1.0.2",
"@optique/run": "jsr:@optique/run@^1.0.2",
"@optique/config": "jsr:@optique/config@^1.1.0",
"@optique/core": "jsr:@optique/core@^1.1.0",
"@optique/discover": "jsr:@optique/discover@^1.1.0",
"@optique/run": "jsr:@optique/run@^1.1.0",
"@standard-schema/spec": "jsr:@standard-schema/spec@^1.1.0",
"@std/assert": "jsr:@std/assert@^1.0.13",
"@std/async": "jsr:@std/async@^1.0.13",
Expand Down
34 changes: 22 additions & 12 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"@fxts/core": "catalog:",
"@optique/config": "catalog:",
"@optique/core": "catalog:",
"@optique/discover": "catalog:",
"@optique/run": "catalog:",
"@standard-schema/spec": "catalog:",
"@hongminhee/localtunnel": "^0.3.0",
Expand Down
18 changes: 11 additions & 7 deletions packages/cli/src/bench/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,19 +159,23 @@ tolerance and measured noise band.`,
},
);

export const benchCommand = command(
"bench",
or(compareParser, runParser),
{
brief: message`Benchmark a Fedify federation workload.`,
description: message`Run an ActivityPub-specific load benchmark against a \
export const benchOptions = or(compareParser, runParser);

export const benchMetadata = {
brief: message`Benchmark a Fedify federation workload.`,
description: message`Run an ActivityPub-specific load benchmark against a \
cooperative Fedify target running in benchmark mode.

The suite file declares the target, actors, and scenarios. This version \
executes the \`inbox\`, \`webfinger\`, \`actor\`, \`object\`, \`fanout\`, \
\`failure\`, and \`mixed\` scenario types; \`collection\` remains reserved by \
the suite format.`,
},
};

export const benchCommand = command(
"bench",
benchOptions,
benchMetadata,
);

export type BenchCommand = InferValue<typeof benchCommand>;
Expand Down
176 changes: 176 additions & 0 deletions packages/cli/src/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { initOptions, runInit } from "@fedify/init";
import {
constant,
merge,
message,
object,
optionNames,
type Parser,
} from "@optique/core";
import {
type CommandMetadata,
type CommandPath,
defineCommand,
type StaticCommand,
} from "@optique/discover";
import { benchMetadata, benchOptions } from "./bench/command.ts";
import {
generateVocabMetadata,
generateVocabOptions,
} from "./generate-vocab/command.ts";
import { inboxMetadata, inboxOptions } from "./inbox/command.ts";
import { lookupMetadata, lookupOptions } from "./lookup/command.ts";
import { nodeInfoMetadata, nodeInfoOptions, runNodeInfo } from "./nodeinfo.ts";
import type { GlobalOptions } from "./options.ts";
import { relayMetadata, relayOptions } from "./relay/command.ts";
import { runTunnel, tunnelMetadata, tunnelOptions } from "./tunnel.ts";
import { webFingerMetadata, webFingerOptions } from "./webfinger/command.ts";

type CliCommandHandler<TValue extends object> = (
value: TValue & GlobalOptions,
) => unknown | Promise<unknown>;

export type CliStaticCommand<TValue extends object> =
& Omit<StaticCommand<"sync", TValue>, "handler">
& {
readonly handler: (value: never) => unknown | Promise<unknown>;
readonly run: CliCommandHandler<TValue>;
};

export type AnyCliStaticCommand =
& Omit<StaticCommand<"sync", never>, "handler" | "parser">
& {
readonly parser: Parser<"sync", unknown, unknown>;
readonly handler: (value: never) => unknown | Promise<unknown>;
readonly run: (value: never) => unknown | Promise<unknown>;
};

export type CliCommandValue<TCommand extends AnyCliStaticCommand> =
TCommand extends CliStaticCommand<infer TValue> ? TValue : never;

function defineCliCommand<const TValue extends object>(
command: {
readonly path: CommandPath;
readonly parser: Parser<"sync", TValue, unknown>;
readonly metadata?: CommandMetadata;
readonly run: CliCommandHandler<NoInfer<TValue>>;
},
): CliStaticCommand<TValue> {
const { run, ...definition } = command;
return {
...defineCommand({
...definition,
handler: (_value: TValue) => {},
}),
run,
};
}

const initParser = merge(
initOptions,
object({ command: constant("init") }),
);

const initMetadata = {
brief: message`Initialize a new Fedify project directory.`,
description: message`Initialize a new Fedify project directory.

By default, it initializes the current directory. You can specify a different directory as an argument.

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.`,
};

export const generatingCommands = [
defineCliCommand({
path: ["init"],
parser: initParser,
metadata: initMetadata,
run: runInit,
}),
defineCliCommand({
path: ["generate-vocab"],
parser: generateVocabOptions,
metadata: generateVocabMetadata,
run: async (value) => {
const { default: runGenerateVocab } = await import(
"./generate-vocab/action.ts"
);
return await runGenerateVocab(value);
},
}),
] satisfies readonly AnyCliStaticCommand[];

export const activityPubCommands = [
defineCliCommand({
path: ["webfinger"],
parser: webFingerOptions,
metadata: webFingerMetadata,
run: async (value) => {
const { default: runWebFinger } = await import("./webfinger/action.ts");
return await runWebFinger(value);
},
}),
defineCliCommand({
path: ["lookup"],
parser: lookupOptions,
metadata: lookupMetadata,
run: async (value) => {
const { runLookup } = await import("./lookup.ts");
return await runLookup(value);
},
}),
defineCliCommand({
path: ["inbox"],
parser: inboxOptions,
metadata: inboxMetadata,
run: async (value) => {
const { runInbox } = await import("./inbox.tsx");
return await runInbox(value);
},
}),
defineCliCommand({
path: ["nodeinfo"],
parser: nodeInfoOptions,
metadata: nodeInfoMetadata,
run: runNodeInfo,
}),
defineCliCommand({
path: ["relay"],
parser: relayOptions,
metadata: relayMetadata,
run: async (value) => {
const { runRelay } = await import("./relay.ts");
return await runRelay(value);
},
}),
defineCliCommand({
path: ["bench"],
parser: benchOptions,
metadata: benchMetadata,
run: async (value) => {
const { runBench } = await import("./bench/mod.ts");
return await runBench(value);
},
}),
] satisfies readonly AnyCliStaticCommand[];

export const networkCommands = [
defineCliCommand({
path: ["tunnel"],
parser: tunnelOptions,
metadata: tunnelMetadata,
run: runTunnel,
}),
] satisfies readonly AnyCliStaticCommand[];

export const cliCommands = [
...generatingCommands,
...activityPubCommands,
...networkCommands,
] as const satisfies readonly AnyCliStaticCommand[];

export type CliCommand = typeof cliCommands[number];
20 changes: 12 additions & 8 deletions packages/cli/src/generate-vocab/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,20 @@ const generatedPath = argument(
},
);

export const generateVocabOptions = object("Generation options", {
command: constant("generate-vocab"),
schemaDir,
generatedPath,
});

export const generateVocabMetadata = {
description: message`Generate vocabulary classes from schema files.`,
};

const generateVocabCommand = command(
"generate-vocab",
object("Generation options", {
command: constant("generate-vocab"),
schemaDir,
generatedPath,
}),
{
description: message`Generate vocabulary classes from schema files.`,
},
generateVocabOptions,
generateVocabMetadata,
);

export default generateVocabCommand;
Expand Down
Loading
Loading