@fedify/cli 2.0.0-pr.458.1788 → 2.0.0-pr.458.1796
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/deno.json +1 -1
- package/dist/deno.js +1 -1
- package/dist/generate-vocab/action.js +4 -4
- package/dist/generate-vocab/command.js +12 -4
- package/dist/mod.js +1 -1
- package/dist/tunnel.js +42 -8
- package/package.json +5 -4
- package/src/generate-vocab/action.ts +4 -4
- package/src/generate-vocab/command.ts +5 -5
- package/src/globals.ts +3 -3
- package/src/mod.ts +1 -1
- package/src/tunnel.test.ts +157 -0
- package/src/tunnel.ts +63 -5
package/deno.json
CHANGED
package/dist/deno.js
CHANGED
|
@@ -8,12 +8,12 @@ import { message } from "@optique/core/message";
|
|
|
8
8
|
import process from "node:process";
|
|
9
9
|
|
|
10
10
|
//#region src/generate-vocab/action.ts
|
|
11
|
-
async function runGenerateVocab({
|
|
12
|
-
if (!(await stat(
|
|
13
|
-
printError(message`${
|
|
11
|
+
async function runGenerateVocab({ schemaDir, generatedPath }) {
|
|
12
|
+
if (!(await stat(schemaDir)).isDirectory()) {
|
|
13
|
+
printError(message`${schemaDir} is not a directory.`);
|
|
14
14
|
process.exit(1);
|
|
15
15
|
}
|
|
16
|
-
await generateVocab(
|
|
16
|
+
await generateVocab(schemaDir, generatedPath);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
//#endregion
|
|
@@ -5,13 +5,21 @@ import { command, constant, message, object, option } from "@optique/core";
|
|
|
5
5
|
import { path } from "@optique/run";
|
|
6
6
|
|
|
7
7
|
//#region src/generate-vocab/command.ts
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
const schemaDir = option("-i", "--input", path({
|
|
9
|
+
metavar: "DIR",
|
|
10
|
+
type: "directory",
|
|
11
|
+
mustExist: true
|
|
12
|
+
}));
|
|
13
|
+
const generatedPath = option("-o", "--output", path({
|
|
14
|
+
metavar: "PATH",
|
|
15
|
+
type: "file",
|
|
16
|
+
allowCreate: true
|
|
17
|
+
}));
|
|
10
18
|
const generateVocabCommand = command("generate-vocab", object({
|
|
11
19
|
command: constant("generate-vocab"),
|
|
12
|
-
|
|
20
|
+
schemaDir,
|
|
13
21
|
generatedPath
|
|
14
|
-
}), { description: message`Generate
|
|
22
|
+
}), { description: message`Generate vocabulary classes from schema files.` });
|
|
15
23
|
var command_default = generateVocabCommand;
|
|
16
24
|
|
|
17
25
|
//#endregion
|
package/dist/mod.js
CHANGED
|
@@ -30,7 +30,7 @@ async function main() {
|
|
|
30
30
|
if (result.command === "webfinger") await runWebFinger(result);
|
|
31
31
|
if (result.command === "inbox") runInbox(result);
|
|
32
32
|
if (result.command === "nodeinfo") runNodeInfo(result);
|
|
33
|
-
if (result.command === "tunnel") runTunnel(result);
|
|
33
|
+
if (result.command === "tunnel") await runTunnel(result);
|
|
34
34
|
if (result.command === "generate-vocab") await runGenerateVocab(result);
|
|
35
35
|
}
|
|
36
36
|
await main();
|
package/dist/tunnel.js
CHANGED
|
@@ -1,20 +1,54 @@
|
|
|
1
1
|
|
|
2
2
|
import { Temporal } from "@js-temporal/polyfill";
|
|
3
3
|
|
|
4
|
-
import { debugOption } from "./globals.js";
|
|
5
|
-
import { argument, command, constant, integer, merge, message, object } from "@optique/core";
|
|
4
|
+
import { configureLogging, debugOption } from "./globals.js";
|
|
5
|
+
import { argument, command, constant, integer, merge, message, object, option, optional } from "@optique/core";
|
|
6
|
+
import { print, printError } from "@optique/run";
|
|
7
|
+
import process from "node:process";
|
|
8
|
+
import ora from "ora";
|
|
9
|
+
import { openTunnel } from "@hongminhee/localtunnel";
|
|
10
|
+
import { choice as choice$1 } from "@optique/core/valueparser";
|
|
6
11
|
|
|
7
12
|
//#region src/tunnel.ts
|
|
8
|
-
const tunnelCommand = command("tunnel", merge(object({
|
|
9
|
-
command: constant("tunnel"),
|
|
13
|
+
const tunnelCommand = command("tunnel", merge("Tunnel options", object({ command: constant("tunnel") }), object({
|
|
10
14
|
port: argument(integer({
|
|
11
15
|
metavar: "PORT",
|
|
12
16
|
min: 0,
|
|
13
17
|
max: 65535
|
|
14
|
-
}))
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
}), { description: message`The local port number to expose.` }),
|
|
19
|
+
service: optional(option("-s", "--service", choice$1([
|
|
20
|
+
"localhost.run",
|
|
21
|
+
"serveo.net",
|
|
22
|
+
"pinggy.io"
|
|
23
|
+
]), { description: message`The localtunnel service to use.` }))
|
|
24
|
+
}), debugOption), { description: message`Expose a local HTTP server to the public internet using a secure tunnel.\nNote that the HTTP requests through the tunnel have X-Forwarded-* headers.` });
|
|
25
|
+
async function runTunnel(command$1, deps = {
|
|
26
|
+
openTunnel,
|
|
27
|
+
ora,
|
|
28
|
+
exit: process.exit
|
|
29
|
+
}) {
|
|
30
|
+
if (command$1.debug) await configureLogging();
|
|
31
|
+
const spinner = deps.ora({
|
|
32
|
+
text: "Creating a secure tunnel...",
|
|
33
|
+
discardStdin: false
|
|
34
|
+
}).start();
|
|
35
|
+
let tunnel;
|
|
36
|
+
try {
|
|
37
|
+
tunnel = await deps.openTunnel({
|
|
38
|
+
port: command$1.port,
|
|
39
|
+
service: command$1.service
|
|
40
|
+
});
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (command$1.debug) printError(message`${String(error)}`);
|
|
43
|
+
spinner.fail("Failed to create a secure tunnel.");
|
|
44
|
+
deps.exit(1);
|
|
45
|
+
}
|
|
46
|
+
spinner.succeed(`Your local server at ${command$1.port} is now publicly accessible:\n`);
|
|
47
|
+
print(message`${tunnel.url.href}`);
|
|
48
|
+
print(message`\nPress ^C to close the tunnel.`);
|
|
49
|
+
process.on("SIGINT", async () => {
|
|
50
|
+
await tunnel.close();
|
|
51
|
+
});
|
|
18
52
|
}
|
|
19
53
|
|
|
20
54
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/cli",
|
|
3
|
-
"version": "2.0.0-pr.458.
|
|
3
|
+
"version": "2.0.0-pr.458.1796+f091195d",
|
|
4
4
|
"description": "CLI toolchain for Fedify and debugging ActivityPub",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fedify",
|
|
@@ -71,9 +71,10 @@
|
|
|
71
71
|
"ora": "^8.2.0",
|
|
72
72
|
"shiki": "^1.6.4",
|
|
73
73
|
"srvx": "^0.8.7",
|
|
74
|
-
"@fedify/
|
|
75
|
-
"@fedify/
|
|
76
|
-
"@fedify/
|
|
74
|
+
"@fedify/fedify": "2.0.0-pr.458.1796+f091195d",
|
|
75
|
+
"@fedify/sqlite": "2.0.0-pr.458.1796+f091195d",
|
|
76
|
+
"@fedify/vocab-runtime": "2.0.0-pr.458.1796+f091195d",
|
|
77
|
+
"@fedify/vocab-tools": "2.0.0-pr.458.1796+f091195d"
|
|
77
78
|
},
|
|
78
79
|
"devDependencies": {
|
|
79
80
|
"@types/bun": "^1.2.23",
|
|
@@ -6,12 +6,12 @@ import process from "node:process";
|
|
|
6
6
|
import type { GenerateVocabCommand } from "./command.ts";
|
|
7
7
|
|
|
8
8
|
export default async function runGenerateVocab(
|
|
9
|
-
{
|
|
9
|
+
{ schemaDir, generatedPath }: GenerateVocabCommand,
|
|
10
10
|
) {
|
|
11
|
-
if (!(await stat(
|
|
12
|
-
printError(message`${
|
|
11
|
+
if (!(await stat(schemaDir)).isDirectory()) {
|
|
12
|
+
printError(message`${schemaDir} is not a directory.`);
|
|
13
13
|
process.exit(1);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
await generateVocab(
|
|
16
|
+
await generateVocab(schemaDir, generatedPath);
|
|
17
17
|
}
|
|
@@ -8,26 +8,26 @@ import {
|
|
|
8
8
|
} from "@optique/core";
|
|
9
9
|
import { path } from "@optique/run";
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const schemaDir = option(
|
|
12
12
|
"-i",
|
|
13
13
|
"--input",
|
|
14
|
-
path({ metavar: "
|
|
14
|
+
path({ metavar: "DIR", type: "directory", mustExist: true }),
|
|
15
15
|
);
|
|
16
16
|
const generatedPath = option(
|
|
17
17
|
"-o",
|
|
18
18
|
"--output",
|
|
19
|
-
path({ metavar: "
|
|
19
|
+
path({ metavar: "PATH", type: "file", allowCreate: true }),
|
|
20
20
|
);
|
|
21
21
|
|
|
22
22
|
const generateVocabCommand = command(
|
|
23
23
|
"generate-vocab",
|
|
24
24
|
object({
|
|
25
25
|
command: constant("generate-vocab"),
|
|
26
|
-
|
|
26
|
+
schemaDir,
|
|
27
27
|
generatedPath,
|
|
28
28
|
}),
|
|
29
29
|
{
|
|
30
|
-
description: message`Generate
|
|
30
|
+
description: message`Generate vocabulary classes from schema files.`,
|
|
31
31
|
},
|
|
32
32
|
);
|
|
33
33
|
|
package/src/globals.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { message, object, option } from "@optique/core";
|
|
2
|
-
import { configure, getConsoleSink } from "@logtape/logtape";
|
|
3
1
|
import { getFileSink } from "@logtape/file";
|
|
4
|
-
import {
|
|
2
|
+
import { configure, getConsoleSink } from "@logtape/logtape";
|
|
3
|
+
import { message, object, option } from "@optique/core";
|
|
5
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
5
|
import process from "node:process";
|
|
6
|
+
import { recordingSink } from "./log.ts";
|
|
7
7
|
|
|
8
8
|
export const debugOption = object("Global options", {
|
|
9
9
|
debug: option("-d", "--debug", {
|
package/src/mod.ts
CHANGED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import type { Tunnel, TunnelOptions } from "@hongminhee/localtunnel";
|
|
2
|
+
import { run } from "@optique/run";
|
|
3
|
+
import { deepEqual, rejects } from "node:assert/strict";
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
import type { Ora } from "ora";
|
|
6
|
+
import { runTunnel, tunnelCommand } from "./tunnel.ts";
|
|
7
|
+
|
|
8
|
+
test("tunnel command structure", () => {
|
|
9
|
+
const testCommandWithOptions = run(tunnelCommand, {
|
|
10
|
+
args: ["tunnel", "3001", "-s", "pinggy.io", "-d"],
|
|
11
|
+
});
|
|
12
|
+
const testCommandWithoutOptions = run(tunnelCommand, {
|
|
13
|
+
args: ["tunnel", "3000"],
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
deepEqual(testCommandWithOptions.command, "tunnel");
|
|
17
|
+
deepEqual(testCommandWithOptions.port, 3001);
|
|
18
|
+
deepEqual(testCommandWithOptions.service, "pinggy.io");
|
|
19
|
+
deepEqual(testCommandWithOptions.debug, true);
|
|
20
|
+
|
|
21
|
+
deepEqual(testCommandWithoutOptions.port, 3000);
|
|
22
|
+
deepEqual(testCommandWithoutOptions.service, undefined);
|
|
23
|
+
deepEqual(testCommandWithoutOptions.debug, false);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("tunnel successfully creates and manages tunnel", async () => {
|
|
27
|
+
const mockCommand = {
|
|
28
|
+
command: "tunnel" as const,
|
|
29
|
+
port: 3001,
|
|
30
|
+
service: "pinggy.io" as const,
|
|
31
|
+
debug: true,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const mockTunnel: Tunnel = {
|
|
35
|
+
url: new URL("https://droar-218-152-125-59.a.free.pinggy.link/"),
|
|
36
|
+
localPort: 3001,
|
|
37
|
+
pid: 123,
|
|
38
|
+
close: () => Promise.resolve(),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
let openTunnelCalled = false;
|
|
42
|
+
let openTunnelPort;
|
|
43
|
+
let openTunnelService;
|
|
44
|
+
let spinnerCalled = false;
|
|
45
|
+
let openTunnelSucceed = false;
|
|
46
|
+
let openTunnelFailed = false;
|
|
47
|
+
let spinnerMsg;
|
|
48
|
+
|
|
49
|
+
const mockDeps = {
|
|
50
|
+
openTunnel: (args: TunnelOptions) => {
|
|
51
|
+
openTunnelCalled = true;
|
|
52
|
+
openTunnelPort = args.port;
|
|
53
|
+
openTunnelService = args.service;
|
|
54
|
+
return Promise.resolve(mockTunnel);
|
|
55
|
+
},
|
|
56
|
+
ora: () =>
|
|
57
|
+
({
|
|
58
|
+
start() {
|
|
59
|
+
spinnerCalled = true;
|
|
60
|
+
return this;
|
|
61
|
+
},
|
|
62
|
+
fail(msg: string) {
|
|
63
|
+
openTunnelFailed = true;
|
|
64
|
+
spinnerMsg = msg;
|
|
65
|
+
return this;
|
|
66
|
+
},
|
|
67
|
+
succeed(msg: string) {
|
|
68
|
+
openTunnelSucceed = true;
|
|
69
|
+
spinnerMsg = msg;
|
|
70
|
+
return this;
|
|
71
|
+
},
|
|
72
|
+
}) as unknown as Ora,
|
|
73
|
+
exit: (): never => {
|
|
74
|
+
throw new Error();
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
await runTunnel(mockCommand, mockDeps);
|
|
80
|
+
} finally {
|
|
81
|
+
deepEqual(openTunnelCalled, true);
|
|
82
|
+
deepEqual(openTunnelPort, 3001);
|
|
83
|
+
deepEqual(openTunnelService, "pinggy.io");
|
|
84
|
+
deepEqual(openTunnelSucceed, true);
|
|
85
|
+
deepEqual(openTunnelFailed, false);
|
|
86
|
+
deepEqual(spinnerCalled, true);
|
|
87
|
+
deepEqual(
|
|
88
|
+
spinnerMsg,
|
|
89
|
+
`Your local server at ${mockTunnel.localPort} is now publicly accessible:\n`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("tunnel fails to create a secure tunnel and handles error", async () => {
|
|
95
|
+
const mockCommand = {
|
|
96
|
+
command: "tunnel" as const,
|
|
97
|
+
port: 3001,
|
|
98
|
+
service: undefined,
|
|
99
|
+
debug: false,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
let openTunnelCalled = false;
|
|
103
|
+
let openTunnelPort;
|
|
104
|
+
let openTunnelService;
|
|
105
|
+
let spinnerCalled = false;
|
|
106
|
+
let openTunnelSucceed = false;
|
|
107
|
+
let openTunnelFailed = false;
|
|
108
|
+
let spinnerMsg;
|
|
109
|
+
|
|
110
|
+
const mockDeps = {
|
|
111
|
+
openTunnel: (args: TunnelOptions) => {
|
|
112
|
+
openTunnelCalled = true;
|
|
113
|
+
openTunnelPort = args.port;
|
|
114
|
+
openTunnelService = args.service;
|
|
115
|
+
return Promise.reject();
|
|
116
|
+
},
|
|
117
|
+
ora: () =>
|
|
118
|
+
({
|
|
119
|
+
start() {
|
|
120
|
+
spinnerCalled = true;
|
|
121
|
+
return this;
|
|
122
|
+
},
|
|
123
|
+
fail(msg: string) {
|
|
124
|
+
openTunnelFailed = true;
|
|
125
|
+
spinnerMsg = msg;
|
|
126
|
+
return this;
|
|
127
|
+
},
|
|
128
|
+
succeed(msg: string) {
|
|
129
|
+
openTunnelSucceed = true;
|
|
130
|
+
spinnerMsg = msg;
|
|
131
|
+
return this;
|
|
132
|
+
},
|
|
133
|
+
}) as unknown as Ora,
|
|
134
|
+
exit: (): never => {
|
|
135
|
+
throw new Error("Process exit called");
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
await rejects(
|
|
141
|
+
() => runTunnel(mockCommand, mockDeps),
|
|
142
|
+
Error,
|
|
143
|
+
"Process exit called",
|
|
144
|
+
);
|
|
145
|
+
} finally {
|
|
146
|
+
deepEqual(openTunnelCalled, true);
|
|
147
|
+
deepEqual(openTunnelPort, 3001);
|
|
148
|
+
deepEqual(openTunnelService, undefined);
|
|
149
|
+
deepEqual(openTunnelSucceed, false);
|
|
150
|
+
deepEqual(openTunnelFailed, true);
|
|
151
|
+
deepEqual(spinnerCalled, true);
|
|
152
|
+
deepEqual(
|
|
153
|
+
spinnerMsg,
|
|
154
|
+
"Failed to create a secure tunnel.",
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
});
|
package/src/tunnel.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { openTunnel, type Tunnel } from "@hongminhee/localtunnel";
|
|
1
2
|
import {
|
|
2
3
|
argument,
|
|
3
4
|
command,
|
|
@@ -7,26 +8,83 @@ import {
|
|
|
7
8
|
merge,
|
|
8
9
|
message,
|
|
9
10
|
object,
|
|
11
|
+
option,
|
|
12
|
+
optional,
|
|
10
13
|
} from "@optique/core";
|
|
11
|
-
import {
|
|
14
|
+
import { choice } from "@optique/core/valueparser";
|
|
15
|
+
import { print, printError } from "@optique/run";
|
|
16
|
+
import process from "node:process";
|
|
17
|
+
import ora from "ora";
|
|
18
|
+
import { configureLogging, debugOption } from "./globals.ts";
|
|
12
19
|
|
|
13
20
|
export const tunnelCommand = command(
|
|
14
21
|
"tunnel",
|
|
15
22
|
merge(
|
|
23
|
+
"Tunnel options",
|
|
16
24
|
object({
|
|
17
25
|
command: constant("tunnel"),
|
|
18
|
-
|
|
26
|
+
}),
|
|
27
|
+
object({
|
|
28
|
+
port: argument(integer({ metavar: "PORT", min: 0, max: 65535 }), {
|
|
29
|
+
description: message`The local port number to expose.`,
|
|
30
|
+
}),
|
|
31
|
+
service: optional(
|
|
32
|
+
option(
|
|
33
|
+
"-s",
|
|
34
|
+
"--service",
|
|
35
|
+
choice(["localhost.run", "serveo.net", "pinggy.io"]),
|
|
36
|
+
{
|
|
37
|
+
description: message`The localtunnel service to use.`,
|
|
38
|
+
},
|
|
39
|
+
),
|
|
40
|
+
),
|
|
19
41
|
}),
|
|
20
42
|
debugOption,
|
|
21
43
|
),
|
|
22
44
|
{
|
|
23
45
|
description:
|
|
24
|
-
message`Expose a local HTTP server to the public internet using a secure tunnel.`,
|
|
46
|
+
message`Expose a local HTTP server to the public internet using a secure tunnel.\nNote that the HTTP requests through the tunnel have X-Forwarded-* headers.`,
|
|
25
47
|
},
|
|
26
48
|
);
|
|
27
49
|
|
|
28
|
-
export function runTunnel(
|
|
50
|
+
export async function runTunnel(
|
|
29
51
|
command: InferValue<typeof tunnelCommand>,
|
|
52
|
+
deps: {
|
|
53
|
+
openTunnel: typeof openTunnel;
|
|
54
|
+
ora: typeof ora;
|
|
55
|
+
exit: typeof process.exit;
|
|
56
|
+
} = {
|
|
57
|
+
openTunnel,
|
|
58
|
+
ora,
|
|
59
|
+
exit: process.exit,
|
|
60
|
+
},
|
|
30
61
|
) {
|
|
31
|
-
|
|
62
|
+
if (command.debug) {
|
|
63
|
+
await configureLogging();
|
|
64
|
+
}
|
|
65
|
+
const spinner = deps.ora({
|
|
66
|
+
text: "Creating a secure tunnel...",
|
|
67
|
+
discardStdin: false,
|
|
68
|
+
}).start();
|
|
69
|
+
let tunnel: Tunnel;
|
|
70
|
+
try {
|
|
71
|
+
tunnel = await deps.openTunnel({
|
|
72
|
+
port: command.port,
|
|
73
|
+
service: command.service,
|
|
74
|
+
});
|
|
75
|
+
} catch (error) {
|
|
76
|
+
if (command.debug) {
|
|
77
|
+
printError(message`${String(error)}`);
|
|
78
|
+
}
|
|
79
|
+
spinner.fail("Failed to create a secure tunnel.");
|
|
80
|
+
deps.exit(1);
|
|
81
|
+
}
|
|
82
|
+
spinner.succeed(
|
|
83
|
+
`Your local server at ${command.port} is now publicly accessible:\n`,
|
|
84
|
+
);
|
|
85
|
+
print(message`${tunnel.url.href}`);
|
|
86
|
+
print(message`\nPress ^C to close the tunnel.`);
|
|
87
|
+
process.on("SIGINT", async () => {
|
|
88
|
+
await tunnel.close();
|
|
89
|
+
});
|
|
32
90
|
}
|