@timetotest/cli 0.1.1
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/README.md +263 -0
- package/dist/bin/ttt.js +30 -0
- package/dist/bin/ttt.js.map +1 -0
- package/dist/src/commands/ask.js +864 -0
- package/dist/src/commands/ask.js.map +1 -0
- package/dist/src/commands/login.js +257 -0
- package/dist/src/commands/login.js.map +1 -0
- package/dist/src/commands/report.js +25 -0
- package/dist/src/commands/report.js.map +1 -0
- package/dist/src/commands/restart.js +17 -0
- package/dist/src/commands/restart.js.map +1 -0
- package/dist/src/commands/share.js +18 -0
- package/dist/src/commands/share.js.map +1 -0
- package/dist/src/commands/start-test.js +97 -0
- package/dist/src/commands/start-test.js.map +1 -0
- package/dist/src/commands/status.js +17 -0
- package/dist/src/commands/status.js.map +1 -0
- package/dist/src/commands/stream.js +20 -0
- package/dist/src/commands/stream.js.map +1 -0
- package/dist/src/commands/test.js +129 -0
- package/dist/src/commands/test.js.map +1 -0
- package/dist/src/lib/config.js +120 -0
- package/dist/src/lib/config.js.map +1 -0
- package/dist/src/lib/help.js +94 -0
- package/dist/src/lib/help.js.map +1 -0
- package/dist/src/lib/http.js +16 -0
- package/dist/src/lib/http.js.map +1 -0
- package/dist/src/lib/ngrok.js +35 -0
- package/dist/src/lib/ngrok.js.map +1 -0
- package/dist/src/lib/socket.js +16 -0
- package/dist/src/lib/socket.js.map +1 -0
- package/dist/src/lib/tui.js +509 -0
- package/dist/src/lib/tui.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
import fs from "fs-extra";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { createHttpClient } from "../lib/http.js";
|
|
7
|
+
import { connectAndSubscribe } from "../lib/socket.js";
|
|
8
|
+
import { createTunnelIfNeeded } from "../lib/ngrok.js";
|
|
9
|
+
import { printHeader, formatEventLine, printMapping } from "../lib/tui.js";
|
|
10
|
+
export const test = new Command("test")
|
|
11
|
+
.description("Start a test (Frontend | API)")
|
|
12
|
+
.requiredOption("--url <ui-url>", "UI URL to test")
|
|
13
|
+
.option("--type <type>", "ui|api|mixed", "ui")
|
|
14
|
+
.option("--execute", "execute generated test cases", false)
|
|
15
|
+
.option("--mode <mode>", "all|priority|failed_only", "priority")
|
|
16
|
+
.option("--project-id <id>")
|
|
17
|
+
.option("--team-id <id>")
|
|
18
|
+
.option("--docs-url <url>", "OpenAPI/Swagger URL for API testing")
|
|
19
|
+
.option("--no-tunnel", "Disable auto ngrok tunnel for local URLs")
|
|
20
|
+
.option("--report [path]", "Generate and download PDF report to path")
|
|
21
|
+
.option("--share", "Enable public sharing after completion")
|
|
22
|
+
.option("--wait", "Block until completion and set exit code based on result")
|
|
23
|
+
.action(async (opts) => {
|
|
24
|
+
const spinner = ora("Preparing test").start();
|
|
25
|
+
let teardown = null;
|
|
26
|
+
try {
|
|
27
|
+
let url = String(opts.url);
|
|
28
|
+
let docsUrl = opts.docsUrl ? String(opts.docsUrl) : undefined;
|
|
29
|
+
printHeader("Time to Test", [chalk.gray("Starting…")]);
|
|
30
|
+
// Auto-tunnel for local URLs unless disabled
|
|
31
|
+
if (opts.tunnel !== false) {
|
|
32
|
+
const tunnel = await createTunnelIfNeeded(url);
|
|
33
|
+
if (tunnel) {
|
|
34
|
+
teardown = tunnel.stop;
|
|
35
|
+
const publicUrl = tunnel.publicUrl;
|
|
36
|
+
spinner.stop();
|
|
37
|
+
printMapping("ngrok", url, publicUrl);
|
|
38
|
+
spinner.start("Continuing…");
|
|
39
|
+
url = publicUrl;
|
|
40
|
+
}
|
|
41
|
+
if (docsUrl) {
|
|
42
|
+
const t2 = await createTunnelIfNeeded(docsUrl);
|
|
43
|
+
if (t2) {
|
|
44
|
+
const publicUrl2 = t2.publicUrl;
|
|
45
|
+
spinner.stop();
|
|
46
|
+
printMapping("ngrok (docs)", docsUrl, publicUrl2);
|
|
47
|
+
spinner.start("Continuing…");
|
|
48
|
+
docsUrl = publicUrl2;
|
|
49
|
+
const old = teardown;
|
|
50
|
+
teardown = async () => {
|
|
51
|
+
await Promise.all([old?.(), t2.stop()].filter(Boolean));
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const http = createHttpClient();
|
|
57
|
+
spinner.text = "Starting test";
|
|
58
|
+
const body = {
|
|
59
|
+
url,
|
|
60
|
+
type: String(opts.type || "ui"),
|
|
61
|
+
project_id: opts.projectId || undefined,
|
|
62
|
+
team_id: opts.teamId || undefined,
|
|
63
|
+
test_metadata: {
|
|
64
|
+
docs_url: docsUrl || undefined,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
const params = {
|
|
68
|
+
execute_tests: !!opts.execute,
|
|
69
|
+
execution_mode: String(opts.mode || "priority"),
|
|
70
|
+
};
|
|
71
|
+
const resp = await http.post("/api/v1/start-test", body, { params });
|
|
72
|
+
spinner.succeed("Test started");
|
|
73
|
+
const testId = resp.data.test_id;
|
|
74
|
+
console.log(chalk.gray("Test ID"), chalk.white(String(testId)));
|
|
75
|
+
const socket = connectAndSubscribe(testId);
|
|
76
|
+
socket.on("test_started", (p) => console.log(formatEventLine("test_started", p)));
|
|
77
|
+
socket.on("test_progress", (p) => console.log(formatEventLine("test_progress", p)));
|
|
78
|
+
socket.on("test_completed", async (p) => {
|
|
79
|
+
console.log(formatEventLine("test_completed", p));
|
|
80
|
+
const finalStatus = p?.status || "completed";
|
|
81
|
+
if (opts.report) {
|
|
82
|
+
const outPath = typeof opts.report === "string"
|
|
83
|
+
? opts.report
|
|
84
|
+
: path.resolve(process.cwd(), `report-${testId}.pdf`);
|
|
85
|
+
await downloadPdfReport(http, testId, outPath);
|
|
86
|
+
console.log(chalk.green(`Report saved: ${outPath}`));
|
|
87
|
+
}
|
|
88
|
+
if (opts.share) {
|
|
89
|
+
try {
|
|
90
|
+
await http.post(`/api/v1/tests/${testId}/share`);
|
|
91
|
+
}
|
|
92
|
+
catch { }
|
|
93
|
+
}
|
|
94
|
+
if (opts.wait) {
|
|
95
|
+
process.exitCode = finalStatus === "passed" ? 0 : 1;
|
|
96
|
+
}
|
|
97
|
+
await socket.disconnect();
|
|
98
|
+
if (teardown)
|
|
99
|
+
await teardown();
|
|
100
|
+
});
|
|
101
|
+
socket.on("test_failed", async (p) => {
|
|
102
|
+
console.log(formatEventLine("test_failed", p));
|
|
103
|
+
if (opts.wait)
|
|
104
|
+
process.exitCode = 1;
|
|
105
|
+
await socket.disconnect();
|
|
106
|
+
if (teardown)
|
|
107
|
+
await teardown();
|
|
108
|
+
});
|
|
109
|
+
if (!opts.wait)
|
|
110
|
+
spinner.info('Use "ttt stream <test-id>" to attach to progress later.');
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
spinner.fail("Failed to start test");
|
|
114
|
+
console.error(err?.response?.data || err?.message || err);
|
|
115
|
+
process.exitCode = 1;
|
|
116
|
+
if (teardown)
|
|
117
|
+
await teardown();
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
async function downloadPdfReport(http, testId, outPath) {
|
|
121
|
+
const createResp = await http.post("/api/v1/reports/", { test_id: testId });
|
|
122
|
+
const reportId = createResp.data?.id || createResp.data?.report_id;
|
|
123
|
+
const pdfResp = await http.get(`/api/v1/reports/${reportId}/pdf`, {
|
|
124
|
+
responseType: "arraybuffer",
|
|
125
|
+
});
|
|
126
|
+
await fs.ensureDir(path.dirname(outPath));
|
|
127
|
+
await fs.writeFile(outPath, pdfResp.data);
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test.js","sourceRoot":"","sources":["../../../src/commands/test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import os from "os";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import fs from "fs-extra";
|
|
4
|
+
let keytar = null;
|
|
5
|
+
try {
|
|
6
|
+
// Optional dependency; may fail in some environments
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
8
|
+
keytar = require("keytar");
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
keytar = null;
|
|
12
|
+
}
|
|
13
|
+
import dotenv from "dotenv";
|
|
14
|
+
dotenv.config();
|
|
15
|
+
const DEFAULT_CONFIG = {
|
|
16
|
+
apiUrl: process.env.TTT_API_URL || "https://dev.timetotest.kinra.ng",
|
|
17
|
+
socketUrl: process.env.TTT_SOCKET_URL ||
|
|
18
|
+
process.env.TTT_API_URL ||
|
|
19
|
+
"https://dev.timetotest.kinra.ng",
|
|
20
|
+
auth: { firebaseIdToken: process.env.TTT_TOKEN || null, expiresAt: null },
|
|
21
|
+
defaultProjectId: null,
|
|
22
|
+
defaultTeamId: null,
|
|
23
|
+
lastProjectByUser: {},
|
|
24
|
+
ngrok: { authtoken: process.env.TTT_NGROK_AUTHTOKEN || null },
|
|
25
|
+
};
|
|
26
|
+
const CONFIG_DIR = path.join(os.homedir(), ".timetotest");
|
|
27
|
+
const CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
|
|
28
|
+
export function loadConfig() {
|
|
29
|
+
try {
|
|
30
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
31
|
+
const file = fs.readFileSync(CONFIG_PATH, "utf-8");
|
|
32
|
+
const parsed = JSON.parse(file);
|
|
33
|
+
return {
|
|
34
|
+
...DEFAULT_CONFIG,
|
|
35
|
+
...parsed,
|
|
36
|
+
auth: { ...DEFAULT_CONFIG.auth, ...(parsed.auth || {}) },
|
|
37
|
+
ngrok: { ...DEFAULT_CONFIG.ngrok, ...(parsed.ngrok || {}) },
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// ignore
|
|
43
|
+
}
|
|
44
|
+
return { ...DEFAULT_CONFIG };
|
|
45
|
+
}
|
|
46
|
+
export function saveConfig(config) {
|
|
47
|
+
fs.ensureDirSync(CONFIG_DIR);
|
|
48
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
49
|
+
}
|
|
50
|
+
export function setUserLastProject(userEmail, projectId) {
|
|
51
|
+
const cfg = loadConfig();
|
|
52
|
+
cfg.lastProjectByUser = cfg.lastProjectByUser || {};
|
|
53
|
+
cfg.lastProjectByUser[userEmail] = projectId;
|
|
54
|
+
saveConfig(cfg);
|
|
55
|
+
}
|
|
56
|
+
export function getUserLastProject(userEmail) {
|
|
57
|
+
const cfg = loadConfig();
|
|
58
|
+
const map = cfg.lastProjectByUser || {};
|
|
59
|
+
const v = map[userEmail];
|
|
60
|
+
return typeof v === "number" ? v : undefined;
|
|
61
|
+
}
|
|
62
|
+
export function getAuthToken() {
|
|
63
|
+
// Prefer secure store
|
|
64
|
+
const service = "timetotest-cli";
|
|
65
|
+
const account = os.userInfo().username || "default";
|
|
66
|
+
try {
|
|
67
|
+
if (keytar) {
|
|
68
|
+
const v = keytar.getPassword(service, account);
|
|
69
|
+
if (v && typeof v.then === "function") {
|
|
70
|
+
// keytar in ESM returns Promise; we cannot block here, so fall back to file for now
|
|
71
|
+
}
|
|
72
|
+
else if (typeof v === "string") {
|
|
73
|
+
return v;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// ignore
|
|
79
|
+
}
|
|
80
|
+
const cfg = loadConfig();
|
|
81
|
+
return cfg.auth.firebaseIdToken || null;
|
|
82
|
+
}
|
|
83
|
+
export function setAuthToken(token, expiresAt) {
|
|
84
|
+
const cfg = loadConfig();
|
|
85
|
+
cfg.auth.firebaseIdToken = token;
|
|
86
|
+
cfg.auth.expiresAt = typeof expiresAt === "number" ? expiresAt : null;
|
|
87
|
+
saveConfig(cfg);
|
|
88
|
+
// Best-effort secure store
|
|
89
|
+
try {
|
|
90
|
+
if (keytar) {
|
|
91
|
+
const service = "timetotest-cli";
|
|
92
|
+
const account = os.userInfo().username || "default";
|
|
93
|
+
keytar.setPassword(service, account, token);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// ignore
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export function resolveApiUrl(override) {
|
|
101
|
+
const cfg = loadConfig();
|
|
102
|
+
const url = override || process.env.TTT_API_URL || cfg.apiUrl;
|
|
103
|
+
if (url.startsWith("http://") && process.env.TTT_ALLOW_INSECURE !== "true") {
|
|
104
|
+
throw new Error("Insecure API URL (http://) not allowed. Set TTT_ALLOW_INSECURE=true to override.");
|
|
105
|
+
}
|
|
106
|
+
return url;
|
|
107
|
+
}
|
|
108
|
+
export function resolveSocketUrl(override) {
|
|
109
|
+
const cfg = loadConfig();
|
|
110
|
+
const url = override || process.env.TTT_SOCKET_URL || cfg.socketUrl;
|
|
111
|
+
if (url.startsWith("http://") && process.env.TTT_ALLOW_INSECURE !== "true") {
|
|
112
|
+
throw new Error("Insecure Socket URL (http://) not allowed. Set TTT_ALLOW_INSECURE=true to override.");
|
|
113
|
+
}
|
|
114
|
+
return url;
|
|
115
|
+
}
|
|
116
|
+
export function getNgrokAuthtoken() {
|
|
117
|
+
const cfg = loadConfig();
|
|
118
|
+
return process.env.TTT_NGROK_AUTHTOKEN || cfg.ngrok.authtoken || null;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/lib/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
function stripAnsi(input) {
|
|
3
|
+
// eslint-disable-next-line no-control-regex
|
|
4
|
+
return input.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
|
|
5
|
+
}
|
|
6
|
+
function boxBlock(lines) {
|
|
7
|
+
const maxLen = Math.max(...lines.map((l) => stripAnsi(l).length), 0);
|
|
8
|
+
const width = Math.min(Math.max(maxLen + 4, 32), 96);
|
|
9
|
+
const top = chalk.gray(`┌${"─".repeat(width - 2)}┐`);
|
|
10
|
+
const bottom = chalk.gray(`└${"─".repeat(width - 2)}┘`);
|
|
11
|
+
const content = lines
|
|
12
|
+
.map((line) => {
|
|
13
|
+
const pad = width - 2 - stripAnsi(line).length;
|
|
14
|
+
return (chalk.gray("│") +
|
|
15
|
+
" " +
|
|
16
|
+
line +
|
|
17
|
+
" ".repeat(Math.max(0, pad - 1)) +
|
|
18
|
+
chalk.gray("│"));
|
|
19
|
+
})
|
|
20
|
+
.join("\n");
|
|
21
|
+
return `${top}\n${content}\n${bottom}`;
|
|
22
|
+
}
|
|
23
|
+
function renderCommands(cmd) {
|
|
24
|
+
if (!cmd.commands || cmd.commands.length === 0)
|
|
25
|
+
return "";
|
|
26
|
+
const entries = cmd.commands
|
|
27
|
+
.filter((c) => !c.name().startsWith("_"))
|
|
28
|
+
.map((c) => ({
|
|
29
|
+
name: [c.name(), ...c.aliases()].filter(Boolean).join(", "),
|
|
30
|
+
desc: c.description() || "",
|
|
31
|
+
}));
|
|
32
|
+
const col1 = Math.min(28, Math.max(...entries.map((e) => stripAnsi(e.name).length), 0) + 2);
|
|
33
|
+
const lines = entries.map((e) => {
|
|
34
|
+
const left = chalk.cyan(e.name);
|
|
35
|
+
const pad = Math.max(0, col1 - stripAnsi(e.name).length);
|
|
36
|
+
const right = chalk.white(e.desc);
|
|
37
|
+
return left + " ".repeat(pad) + right;
|
|
38
|
+
});
|
|
39
|
+
return [chalk.bold("Commands:"), ...lines].join("\n");
|
|
40
|
+
}
|
|
41
|
+
function renderOptions(cmd) {
|
|
42
|
+
const opts = (cmd.options || []);
|
|
43
|
+
const items = [];
|
|
44
|
+
for (const o of opts) {
|
|
45
|
+
items.push({ flags: o.flags, desc: o.description || "" });
|
|
46
|
+
}
|
|
47
|
+
// Include help flag explicitly for discoverability
|
|
48
|
+
items.push({ flags: "-h, --help", desc: "display help for command" });
|
|
49
|
+
const col1 = Math.min(32, Math.max(...items.map((i) => stripAnsi(i.flags).length), 0) + 4);
|
|
50
|
+
const lines = items.map((i) => {
|
|
51
|
+
const left = chalk.yellow(i.flags);
|
|
52
|
+
const pad = Math.max(0, col1 - stripAnsi(i.flags).length);
|
|
53
|
+
const right = chalk.white(i.desc);
|
|
54
|
+
return left + " ".repeat(pad) + right;
|
|
55
|
+
});
|
|
56
|
+
return [chalk.bold("Options:"), ...lines].join("\n");
|
|
57
|
+
}
|
|
58
|
+
function renderUsage(cmd, binName) {
|
|
59
|
+
const parts = [];
|
|
60
|
+
parts.push(chalk.bold("Usage:"));
|
|
61
|
+
const isRoot = !cmd.parent; // root command has no parent
|
|
62
|
+
const name = isRoot ? binName : `${binName} ${cmd.name()}`;
|
|
63
|
+
const args = cmd.usage() || "";
|
|
64
|
+
parts.push(`${chalk.cyan(name)} ${chalk.white(args)}`.trim());
|
|
65
|
+
return parts.join("\n");
|
|
66
|
+
}
|
|
67
|
+
export function applyStyledHelp(cmd, binName = "ttt") {
|
|
68
|
+
const original = cmd.helpInformation.bind(cmd);
|
|
69
|
+
cmd.helpInformation = function styledHelp() {
|
|
70
|
+
const version = cmd._version || "";
|
|
71
|
+
const desc = cmd.description() || "";
|
|
72
|
+
const title = `${chalk.bold("Time to Test CLI")} ${chalk.dim("(" + binName + ")")}`;
|
|
73
|
+
const subtitleLine = [
|
|
74
|
+
version ? chalk.gray("v" + version) : "",
|
|
75
|
+
desc ? chalk.gray(desc) : "",
|
|
76
|
+
]
|
|
77
|
+
.filter(Boolean)
|
|
78
|
+
.join(" — ");
|
|
79
|
+
const header = boxBlock([title, subtitleLine].filter((l) => !!l && stripAnsi(l).length > 0));
|
|
80
|
+
const usage = renderUsage(cmd, binName);
|
|
81
|
+
const commands = renderCommands(cmd);
|
|
82
|
+
const options = renderOptions(cmd);
|
|
83
|
+
const sections = [header, usage, commands, options].filter((s) => s && s.trim().length > 0);
|
|
84
|
+
return sections.join("\n\n") + "\n";
|
|
85
|
+
};
|
|
86
|
+
return original;
|
|
87
|
+
}
|
|
88
|
+
export function applyStyledHelpRecursively(root, binName = "ttt") {
|
|
89
|
+
applyStyledHelp(root, binName);
|
|
90
|
+
for (const child of root.commands) {
|
|
91
|
+
applyStyledHelp(child, binName);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=help.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"help.js","sourceRoot":"","sources":["../../../src/lib/help.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { getAuthToken, resolveApiUrl } from "./config.js";
|
|
3
|
+
export function createHttpClient(baseUrl) {
|
|
4
|
+
const apiUrl = resolveApiUrl(baseUrl);
|
|
5
|
+
const token = getAuthToken();
|
|
6
|
+
const instance = axios.create({ baseURL: apiUrl, timeout: 30000 });
|
|
7
|
+
instance.interceptors.request.use((config) => {
|
|
8
|
+
if (token) {
|
|
9
|
+
config.headers = config.headers || {};
|
|
10
|
+
config.headers["Authorization"] = `Bearer ${token}`;
|
|
11
|
+
}
|
|
12
|
+
return config;
|
|
13
|
+
});
|
|
14
|
+
return instance;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/lib/http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAsB,MAAM,OAAO,CAAC;AAC3C,OAAO,EAAC,YAAY,EAAE,aAAa,EAAC,MAAM,aAAa,CAAC;AAExD,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,YAAY,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,EAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAC,CAAC,CAAC;IACjE,QAAQ,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QAC3C,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;YACtC,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,KAAK,EAAE,CAAC;QACtD,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { connect, authtoken as setNgrokAuthtoken } from '@ngrok/ngrok';
|
|
2
|
+
import { getNgrokAuthtoken } from './config.js';
|
|
3
|
+
export function isLocalUrl(url) {
|
|
4
|
+
try {
|
|
5
|
+
const u = new URL(url);
|
|
6
|
+
return (u.hostname === 'localhost' ||
|
|
7
|
+
u.hostname === '127.0.0.1' ||
|
|
8
|
+
u.hostname.endsWith('.localhost'));
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export async function createTunnelIfNeeded(url) {
|
|
15
|
+
if (!url || !isLocalUrl(url))
|
|
16
|
+
return null;
|
|
17
|
+
const authtoken = getNgrokAuthtoken();
|
|
18
|
+
const port = Number(new URL(url).port || (new URL(url).protocol === 'https:' ? 443 : 80));
|
|
19
|
+
const addr = Number(port) || (new URL(url).protocol === 'https:' ? 443 : 80);
|
|
20
|
+
if (authtoken) {
|
|
21
|
+
await setNgrokAuthtoken(authtoken);
|
|
22
|
+
}
|
|
23
|
+
const tunnel = await connect({ addr, proto: 'http' });
|
|
24
|
+
const publicUrl = tunnel.url();
|
|
25
|
+
if (!publicUrl) {
|
|
26
|
+
throw new Error('Failed to obtain ngrok public URL');
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
publicUrl,
|
|
30
|
+
stop: async () => {
|
|
31
|
+
await tunnel.close();
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=ngrok.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ngrok.js","sourceRoot":"","sources":["../../../src/lib/ngrok.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,IAAI,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAOhD,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,OAAO,CACL,CAAC,CAAC,QAAQ,KAAK,WAAW;YAC1B,CAAC,CAAC,QAAQ,KAAK,WAAW;YAC1B,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,CAClC,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,GAAY;IACrD,IAAI,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,SAAS,GAAG,iBAAiB,EAAE,CAAC;IACtC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1F,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC7E,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,iBAAiB,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,EAAE,CAAC;IAC/B,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IACD,OAAO;QACL,SAAS;QACT,IAAI,EAAE,KAAK,IAAI,EAAE;YACf,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { io } from 'socket.io-client';
|
|
2
|
+
import { getAuthToken, resolveSocketUrl } from './config.js';
|
|
3
|
+
export function connectAndSubscribe(testId, socketUrlOverride) {
|
|
4
|
+
const socketUrl = resolveSocketUrl(socketUrlOverride);
|
|
5
|
+
const token = getAuthToken();
|
|
6
|
+
const socket = io(socketUrl, {
|
|
7
|
+
path: '/socket.io',
|
|
8
|
+
transports: ['websocket', 'polling'],
|
|
9
|
+
auth: token ? { token } : undefined,
|
|
10
|
+
});
|
|
11
|
+
socket.on('connect', () => {
|
|
12
|
+
socket.emit('subscribe_test', { test_id: testId });
|
|
13
|
+
});
|
|
14
|
+
return socket;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=socket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket.js","sourceRoot":"","sources":["../../../src/lib/socket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAU,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAO7D,MAAM,UAAU,mBAAmB,CAAC,MAAuB,EAAE,iBAA0B;IACrF,MAAM,SAAS,GAAG,gBAAgB,CAAC,iBAAiB,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,YAAY,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAG,EAAE,CAAC,SAAS,EAAE;QAC3B,IAAI,EAAE,YAAY;QAClB,UAAU,EAAE,CAAC,WAAW,EAAE,SAAS,CAAC;QACpC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS;KACpC,CAAC,CAAC;IACH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;QACxB,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC"}
|