@n42/cli 0.3.50 → 0.3.68
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/assets/discover.js +2 -3
- package/dist/assets/terminal-light.css +2 -1
- package/dist/assets/terminal.js +67 -32
- package/dist/n42 +1 -1
- package/package.json +1 -1
- package/src/assets/discover.js +2 -3
- package/src/assets/terminal-light.css +2 -1
- package/src/assets/terminal.js +67 -32
package/package.json
CHANGED
package/src/assets/discover.js
CHANGED
|
@@ -64,7 +64,7 @@ function addSvgClickListener() {
|
|
|
64
64
|
else {
|
|
65
65
|
if (!url.startsWith("#")) {
|
|
66
66
|
terminal.open({
|
|
67
|
-
url:
|
|
67
|
+
url: url,
|
|
68
68
|
method: "GET"
|
|
69
69
|
});
|
|
70
70
|
}
|
|
@@ -178,9 +178,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
178
178
|
|
|
179
179
|
terminal = new Terminal({
|
|
180
180
|
title: "Terminal",
|
|
181
|
-
brand: "curl",
|
|
182
181
|
theme: "light",
|
|
183
|
-
pkgVersion: "0.3.
|
|
182
|
+
pkgVersion: "0.3.65"
|
|
184
183
|
});
|
|
185
184
|
|
|
186
185
|
addSvgClickListener();
|
package/src/assets/terminal.js
CHANGED
|
@@ -2,7 +2,6 @@ class Terminal {
|
|
|
2
2
|
constructor(opts = {}) {
|
|
3
3
|
this.opts = {
|
|
4
4
|
title: "Request",
|
|
5
|
-
brand: "curl",
|
|
6
5
|
...opts,
|
|
7
6
|
};
|
|
8
7
|
|
|
@@ -13,6 +12,8 @@ class Terminal {
|
|
|
13
12
|
this._history = [];
|
|
14
13
|
this._historyIndex = -1;
|
|
15
14
|
|
|
15
|
+
this.runningFromFile = window.location.protocol === "file:";
|
|
16
|
+
|
|
16
17
|
// ---- reusable events ----
|
|
17
18
|
this.enterEvent = new KeyboardEvent("keydown", {
|
|
18
19
|
key: "Enter",
|
|
@@ -29,11 +30,21 @@ class Terminal {
|
|
|
29
30
|
which: 27,
|
|
30
31
|
bubbles: true
|
|
31
32
|
});
|
|
33
|
+
|
|
34
|
+
// ---- colors ----
|
|
35
|
+
this.colorBlue="#3f9cff";
|
|
36
|
+
this.colorBrown="#a07c4f";
|
|
37
|
+
this.colorOrange="#f59e0b";
|
|
38
|
+
this.colorPink="#ec4899";
|
|
39
|
+
this.colorGray="#94a3b8";
|
|
40
|
+
this.colorGreen="#4cb56f";
|
|
41
|
+
this.colorRed="#ef4444";
|
|
32
42
|
}
|
|
33
43
|
|
|
34
44
|
async openAndRun(req) {
|
|
35
45
|
this.open(req);
|
|
36
|
-
|
|
46
|
+
|
|
47
|
+
try { await this.httpRequest(req); }
|
|
37
48
|
catch (e) {
|
|
38
49
|
// errors already printed by run(); rethrow if you want
|
|
39
50
|
}
|
|
@@ -53,7 +64,7 @@ class Terminal {
|
|
|
53
64
|
this.overlay.classList.remove("hidden");
|
|
54
65
|
this.dialog.classList.remove("hidden");
|
|
55
66
|
|
|
56
|
-
this.input.value = `
|
|
67
|
+
this.input.value = `request -X ${req.method} ${req.url}`;
|
|
57
68
|
this.input.focus();
|
|
58
69
|
|
|
59
70
|
// prevent background scroll
|
|
@@ -114,14 +125,14 @@ class Terminal {
|
|
|
114
125
|
this._setBusy(false);
|
|
115
126
|
}
|
|
116
127
|
|
|
117
|
-
async
|
|
128
|
+
async httpRequest(req) {
|
|
118
129
|
const normalized = this._normalizeReq(req);
|
|
119
130
|
this._resetTerminal();
|
|
120
131
|
|
|
121
|
-
const cmd = `$
|
|
132
|
+
const cmd = `$ request ${this._getReqArgs(normalized)}`;
|
|
122
133
|
this._addHistory(cmd)
|
|
123
134
|
|
|
124
|
-
this.
|
|
135
|
+
this._printColoredLine(cmd, this.colorBlue);
|
|
125
136
|
this._printLine("");
|
|
126
137
|
|
|
127
138
|
this._setBusy(true);
|
|
@@ -164,7 +175,7 @@ class Terminal {
|
|
|
164
175
|
if (normalized.body) this._printLine(`> (body ${this._byteLen(normalized.body)} bytes)`);
|
|
165
176
|
this._printLine("");
|
|
166
177
|
|
|
167
|
-
this.title.textContent = `Terminal [${normalized.method}]:
|
|
178
|
+
this.title.textContent = `Terminal [${normalized.method}]: request`;
|
|
168
179
|
|
|
169
180
|
const res = await fetch(normalized.url, fetchInit);
|
|
170
181
|
|
|
@@ -275,8 +286,6 @@ class Terminal {
|
|
|
275
286
|
}
|
|
276
287
|
|
|
277
288
|
_handleCommand(cmd) {
|
|
278
|
-
this._printLine(`$ ${cmd}`);
|
|
279
|
-
|
|
280
289
|
if (cmd === "clear") {
|
|
281
290
|
this._addHistory(cmd);
|
|
282
291
|
this._resetTerminal();
|
|
@@ -289,9 +298,21 @@ class Terminal {
|
|
|
289
298
|
return;
|
|
290
299
|
}
|
|
291
300
|
|
|
292
|
-
if (cmd.startsWith("
|
|
293
|
-
|
|
294
|
-
|
|
301
|
+
if (cmd.startsWith("request ")) {
|
|
302
|
+
if (this.runningFromFile) {
|
|
303
|
+
this._addHistory(cmd);
|
|
304
|
+
|
|
305
|
+
this._printColoredLine(`$ ${cmd}`, this.colorBlue);
|
|
306
|
+
|
|
307
|
+
this._printLine("");
|
|
308
|
+
this._printColoredLine("[error]: CORS has blocked this request (the script is running from file://).", this.colorRed);
|
|
309
|
+
this._printLine("Use the arrow keys to reselect the URL, then click the top-right button to open it in a browser tab instead.");
|
|
310
|
+
this._printLine("");
|
|
311
|
+
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
// Parse: request [METHOD] <url>
|
|
315
|
+
const m = cmd.match(/^request\s+(?:-X\s+(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+)?(https?:\/\/\S+)$/i);
|
|
295
316
|
|
|
296
317
|
if (m) {
|
|
297
318
|
const method = m[1] ? m[1].toUpperCase() : "GET";
|
|
@@ -309,12 +330,12 @@ class Terminal {
|
|
|
309
330
|
this._resetTerminal();
|
|
310
331
|
this._printHtml(this._jsonHighlight(discoveryTrace));
|
|
311
332
|
} else {
|
|
312
|
-
this.
|
|
333
|
+
this._printColoredLine("[error]: Discovery trace missing", this.colorRed);
|
|
313
334
|
}
|
|
314
335
|
return;
|
|
315
336
|
}
|
|
316
337
|
|
|
317
|
-
this.
|
|
338
|
+
this._printColoredLine("[error]: Unknown command", this.colorRed);
|
|
318
339
|
}
|
|
319
340
|
|
|
320
341
|
_createImgEl(src, size, alt) {
|
|
@@ -447,13 +468,13 @@ class Terminal {
|
|
|
447
468
|
btnDownload.prepend(downloadImg);
|
|
448
469
|
btnDownload.addEventListener("click", () => this._download());
|
|
449
470
|
|
|
450
|
-
const
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
471
|
+
const openUrlImg = this._createImgEl("../../assets/open-external-light.svg", 24, "Open");
|
|
472
|
+
const btnOpenUrl = document.createElement("button");
|
|
473
|
+
btnOpenUrl.className = "cm-btn";
|
|
474
|
+
btnOpenUrl.type = "button";
|
|
475
|
+
btnOpenUrl.title = "Open URL";
|
|
476
|
+
btnOpenUrl.prepend(openUrlImg);
|
|
477
|
+
btnOpenUrl.addEventListener("click", () => this._openUrl());
|
|
457
478
|
|
|
458
479
|
const themeImg = this._createImgEl("../../assets/theme-light.svg", 24, "Theme");
|
|
459
480
|
const btnTheme = document.createElement("button");
|
|
@@ -463,7 +484,7 @@ class Terminal {
|
|
|
463
484
|
btnTheme.prepend(themeImg);
|
|
464
485
|
btnTheme.addEventListener("click", () => this._toggleTheme());
|
|
465
486
|
|
|
466
|
-
head.append(title, spacer, btnTheme, btnCopy, btnDownload,
|
|
487
|
+
head.append(title, spacer, btnTheme, btnCopy, btnDownload, btnOpenUrl);
|
|
467
488
|
|
|
468
489
|
// Body
|
|
469
490
|
const body = document.createElement("div");
|
|
@@ -513,7 +534,7 @@ class Terminal {
|
|
|
513
534
|
|
|
514
535
|
this.btnCopy = btnCopy;
|
|
515
536
|
this.btnDownload = btnDownload;
|
|
516
|
-
this.
|
|
537
|
+
this.btnOpenUrl = btnOpenUrl;
|
|
517
538
|
this.btnTheme = btnTheme;
|
|
518
539
|
|
|
519
540
|
// Close when clicking outside (optional)
|
|
@@ -562,7 +583,7 @@ class Terminal {
|
|
|
562
583
|
this.statusEl.textContent = isBusy ? "Running…" : "Ready";
|
|
563
584
|
}
|
|
564
585
|
|
|
565
|
-
|
|
586
|
+
_openUrl() {
|
|
566
587
|
const url = this.input.value.replace(/^.*?(?=https?:\/\/)/, "")
|
|
567
588
|
window.open(url, '_blank');
|
|
568
589
|
}
|
|
@@ -582,7 +603,7 @@ class Terminal {
|
|
|
582
603
|
this._printLine("");
|
|
583
604
|
|
|
584
605
|
this._printLine("Available commands:");
|
|
585
|
-
this._printLine("
|
|
606
|
+
this._printLine(" request [-X METHOD] <url> – send HTTP request to a URL");
|
|
586
607
|
this._printLine(" trace – output discovery trace");
|
|
587
608
|
this._printLine(" clear – clear terminal output");
|
|
588
609
|
this._printLine(" help – show this help text");
|
|
@@ -601,13 +622,22 @@ class Terminal {
|
|
|
601
622
|
this._printLine("");
|
|
602
623
|
|
|
603
624
|
this._printLine("Examples:");
|
|
604
|
-
this._printLine("
|
|
605
|
-
this._printLine("
|
|
625
|
+
this._printLine(" request https://api.node42.dev/health");
|
|
626
|
+
this._printLine(" request -X POST https://api.node42.dev/health");
|
|
606
627
|
this._printLine("");
|
|
607
628
|
}
|
|
608
629
|
|
|
609
630
|
_printLine(s) {
|
|
610
|
-
|
|
631
|
+
const html = s.replace(/\r?\n/g, "<br>");
|
|
632
|
+
this.term.innerHTML += `${html}<br>`;
|
|
633
|
+
this._scrollToBottom();
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
_printColoredLine(s, color="#111827") {
|
|
637
|
+
const html = this.term.innerHTML;
|
|
638
|
+
this.term.innerHTML =
|
|
639
|
+
`${html}<span style="color:${color};">${s}</span><br>`;
|
|
640
|
+
|
|
611
641
|
this._scrollToBottom();
|
|
612
642
|
}
|
|
613
643
|
|
|
@@ -617,8 +647,13 @@ class Terminal {
|
|
|
617
647
|
}
|
|
618
648
|
|
|
619
649
|
_printRaw(s) {
|
|
620
|
-
|
|
621
|
-
|
|
650
|
+
const html = s.replace(/\r?\n/g, "<br>");
|
|
651
|
+
this.term.innerHTML += html;
|
|
652
|
+
|
|
653
|
+
if (!s.endsWith("\n")) {
|
|
654
|
+
this.term.innerHTML += "<br>";
|
|
655
|
+
}
|
|
656
|
+
|
|
622
657
|
this._scrollToBottom();
|
|
623
658
|
}
|
|
624
659
|
|
|
@@ -653,14 +688,14 @@ class Terminal {
|
|
|
653
688
|
const url = URL.createObjectURL(blob);
|
|
654
689
|
const a = document.createElement("a");
|
|
655
690
|
a.href = url;
|
|
656
|
-
a.download = `
|
|
691
|
+
a.download = `request-output-${new Date().toISOString().slice(0,19).replace(/[:T]/g,"-")}.txt`;
|
|
657
692
|
document.body.appendChild(a);
|
|
658
693
|
a.click();
|
|
659
694
|
a.remove();
|
|
660
695
|
URL.revokeObjectURL(url);
|
|
661
696
|
}
|
|
662
697
|
|
|
663
|
-
|
|
698
|
+
_getReqArgs(req) {
|
|
664
699
|
// Presentational: escapes minimally for display (not execution-safe for all shells).
|
|
665
700
|
const parts = [];
|
|
666
701
|
parts.push(`-X ${req.method}`);
|