@aimeloic/monkey-tester 3.0.9 → 3.0.11
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/htmlTemplate.js +176 -302
- package/monkey.js +1 -1
- package/package.json +1 -1
package/htmlTemplate.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function getHtmlTemplate(endpoints) {
|
|
2
4
|
const safeJsonString = Buffer.from(JSON.stringify(endpoints)).toString('base64');
|
|
3
5
|
|
|
4
|
-
return
|
|
5
|
-
<!DOCTYPE html>
|
|
6
|
+
return `<!DOCTYPE html>
|
|
6
7
|
<html lang="en">
|
|
7
8
|
<head>
|
|
8
9
|
<meta charset="UTF-8">
|
|
@@ -11,172 +12,63 @@ export function getHtmlTemplate(endpoints) {
|
|
|
11
12
|
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700&family=DM+Mono:wght@400;500&family=DM+Sans:wght@300;400;500&display=swap" rel="stylesheet">
|
|
12
13
|
<style>
|
|
13
14
|
:root {
|
|
14
|
-
--bg: #0e0c09;
|
|
15
|
-
--
|
|
16
|
-
--
|
|
17
|
-
--border: #3a3020;
|
|
18
|
-
--accent: #e8a838;
|
|
19
|
-
--accent2: #c47a1e;
|
|
20
|
-
--text: #f0e8d8;
|
|
21
|
-
--text-dim: #9a8c78;
|
|
22
|
-
--red: #d45c3c;
|
|
23
|
-
--green: #6ba05a;
|
|
24
|
-
--blue: #5a86c0;
|
|
25
|
-
--radius: 8px;
|
|
15
|
+
--bg: #0e0c09; --surface: #181510; --surface2: #221d14; --border: #3a3020;
|
|
16
|
+
--accent: #e8a838; --accent2: #c47a1e; --text: #f0e8d8; --text-dim: #9a8c78;
|
|
17
|
+
--red: #d45c3c; --green: #6ba05a; --blue: #5a86c0; --radius: 8px;
|
|
26
18
|
}
|
|
27
|
-
|
|
28
19
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
background: var(--bg);
|
|
32
|
-
color: var(--text);
|
|
33
|
-
font-family: 'DM Sans', sans-serif;
|
|
34
|
-
font-size: 14px;
|
|
35
|
-
height: 100vh;
|
|
36
|
-
overflow: hidden; /* Prevents whole-page scrolling */
|
|
37
|
-
background-image: radial-gradient(ellipse 80% 60% at 50% -20%, #3a2a0a22 0%, transparent 70%);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
header {
|
|
41
|
-
border-bottom: 1px solid var(--border);
|
|
42
|
-
padding: 16px 32px;
|
|
43
|
-
display: flex;
|
|
44
|
-
align-items: center;
|
|
45
|
-
gap: 20px;
|
|
46
|
-
background: #0e0c09ee;
|
|
47
|
-
backdrop-filter: blur(8px);
|
|
48
|
-
height: 65px;
|
|
49
|
-
}
|
|
50
|
-
|
|
20
|
+
body { background: var(--bg); color: var(--text); font-family: 'DM Sans', sans-serif; font-size: 14px; height: 100vh; overflow: hidden; background-image: radial-gradient(ellipse 80% 60% at 50% -20%, #3a2a0a22 0%, transparent 70%); }
|
|
21
|
+
header { border-bottom: 1px solid var(--border); padding: 16px 32px; display: flex; align-items: center; gap: 20px; background: #0e0c09ee; backdrop-filter: blur(8px); height: 65px; }
|
|
51
22
|
.logo { font-family: 'Playfair Display', serif; font-size: 20px; color: var(--accent); letter-spacing: 0.02em; }
|
|
52
23
|
.logo span { color: var(--text-dim); font-size: 11px; font-family: 'DM Mono', monospace; display: inline-block; margin-left: 8px; font-weight: 400; }
|
|
53
24
|
.header-right { margin-left: auto; display: flex; align-items: center; gap: 16px; }
|
|
54
25
|
.jwt-wrap, .base-url-wrap { display: flex; align-items: center; gap: 8px; }
|
|
55
26
|
.jwt-wrap label, .base-url-wrap label { color: var(--text-dim); font-size: 11px; font-family: 'DM Mono', monospace; letter-spacing: 0.05em; }
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
font-family: 'DM Mono', monospace; font-size: 12px; padding: 6px 12px; border-radius: var(--radius); width: 220px; outline: none;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/* Fixed view height viewport matrix layout grid rules */
|
|
63
|
-
.layout {
|
|
64
|
-
display: grid;
|
|
65
|
-
grid-template-columns: 280px 1fr 450px;
|
|
66
|
-
height: calc(100vh - 65px);
|
|
67
|
-
overflow: hidden;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
aside {
|
|
71
|
-
border-right: 1px solid var(--border);
|
|
72
|
-
overflow-y: auto;
|
|
73
|
-
padding: 16px 0;
|
|
74
|
-
background: #0b0907;
|
|
75
|
-
}
|
|
76
|
-
|
|
27
|
+
#jwt-input, #base-url { background: var(--surface2); border: 1px solid var(--border); color: var(--text); font-family: 'DM Mono', monospace; font-size: 12px; padding: 6px 12px; border-radius: var(--radius); width: 220px; outline: none; }
|
|
28
|
+
.layout { display: grid; grid-template-columns: 280px 1fr 450px; height: calc(100vh - 65px); overflow: hidden; }
|
|
29
|
+
aside { border-right: 1px solid var(--border); overflow-y: auto; padding: 16px 0; background: #0b0907; }
|
|
77
30
|
.section-label { font-size: 10px; font-family: 'DM Mono', monospace; color: var(--text-dim); letter-spacing: 0.12em; text-transform: uppercase; padding: 12px 18px 6px; }
|
|
78
|
-
|
|
79
31
|
.nav-item { display: flex; align-items: center; gap: 10px; padding: 10px 18px; cursor: pointer; border-left: 2px solid transparent; color: var(--text-dim); transition: all 0.2s; }
|
|
80
32
|
.nav-item:hover { background: var(--surface); color: var(--text); }
|
|
81
33
|
.nav-item.active { border-left-color: var(--accent); background: var(--surface); color: var(--accent); }
|
|
82
|
-
|
|
83
34
|
.method-badge { font-family: 'DM Mono', monospace; font-size: 9px; font-weight: 600; padding: 2px 6px; border-radius: 4px; min-width: 52px; text-align: center; text-transform: uppercase; }
|
|
84
|
-
.GET { background: #1a3a22; color: #6ba05a; }
|
|
85
|
-
.POST { background: #1a2e3a; color: #5a86c0; }
|
|
86
|
-
.PUT, .PATCH { background: #3a2e10; color: #e8a838; }
|
|
87
|
-
.DELETE { background: #3a1a14; color: #d45c3c; }
|
|
88
|
-
|
|
35
|
+
.GET { background: #1a3a22; color: #6ba05a; } .POST { background: #1a2e3a; color: #5a86c0; } .PUT, .PATCH { background: #3a2e10; color: #e8a838; } .DELETE { background: #3a1a14; color: #d45c3c; }
|
|
89
36
|
.nav-label { font-size: 12px; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
90
|
-
|
|
91
|
-
main {
|
|
92
|
-
overflow-y: auto;
|
|
93
|
-
padding: 32px;
|
|
94
|
-
background: #0e0c09;
|
|
95
|
-
}
|
|
96
|
-
|
|
37
|
+
main { overflow-y: auto; padding: 32px; background: #0e0c09; }
|
|
97
38
|
.endpoint-title { font-family: 'Playfair Display', serif; font-size: 24px; color: var(--accent); margin-bottom: 8px; }
|
|
98
39
|
.endpoint-path { font-family: 'DM Mono', monospace; font-size: 13px; color: var(--text-dim); margin-bottom: 24px; display: flex; align-items: center; gap: 8px; }
|
|
99
40
|
.endpoint-desc { color: var(--text-dim); font-size: 13px; line-height: 1.6; margin-bottom: 24px; border-left: 2px solid var(--border); padding-left: 12px; }
|
|
100
|
-
|
|
101
41
|
.form-section { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px; margin-bottom: 20px; }
|
|
102
42
|
.form-section-title { font-size: 11px; font-family: 'DM Mono', monospace; color: var(--text-dim); letter-spacing: 0.1em; text-transform: uppercase; margin-bottom: 16px; border-bottom: 1px solid var(--border); padding-bottom: 6px; }
|
|
103
|
-
|
|
104
43
|
.field-row { display: grid; grid-template-columns: 150px 1fr; align-items: center; gap: 16px; margin-bottom: 14px; }
|
|
105
|
-
.field-row:last-child { margin-bottom: 0; }
|
|
106
44
|
.field-label { font-family: 'DM Mono', monospace; font-size: 12px; color: var(--text-dim); text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
107
|
-
|
|
108
|
-
input[type=text], input[type=password], input[type=number], input[type=date], input[type=tel], input[type=url], select {
|
|
109
|
-
background: var(--surface2); border: 1px solid var(--border); color: var(--text); font-family: 'DM Sans', sans-serif; font-size: 13px; padding: 8px 12px; border-radius: var(--radius); width: 100%; outline: none; transition: border-color 0.2s;
|
|
110
|
-
}
|
|
45
|
+
input[type=text], input[type=password], input[type=number], input[type=date], input[type=tel], input[type=url], input[type=email], select { background: var(--surface2); border: 1px solid var(--border); color: var(--text); font-family: 'DM Sans', sans-serif; font-size: 13px; padding: 8px 12px; border-radius: var(--radius); width: 100%; outline: none; transition: border-color 0.2s; }
|
|
111
46
|
input:focus { border-color: var(--accent); }
|
|
112
|
-
|
|
113
47
|
.btn-row { margin-top: 24px; display: flex; gap: 12px; }
|
|
114
48
|
.btn { background: var(--accent); color: #0e0c09; border: none; padding: 10px 24px; border-radius: var(--radius); font-size: 13px; font-weight: 500; cursor: pointer; transition: background-color 0.2s; }
|
|
115
|
-
.btn:hover { background: #f0b850; }
|
|
116
|
-
.
|
|
117
|
-
.btn-secondary:hover { color: var(--text); background: var(--surface); }
|
|
118
|
-
|
|
119
|
-
/* FIXED: Response block scroll logic layout rules */
|
|
120
|
-
.response-panel {
|
|
121
|
-
border-left: 1px solid var(--border);
|
|
122
|
-
display: flex;
|
|
123
|
-
flex-direction: column;
|
|
124
|
-
overflow: hidden;
|
|
125
|
-
background: #110e0a;
|
|
126
|
-
}
|
|
49
|
+
.btn:hover { background: #f0b850; } .btn-secondary { background: var(--surface2); color: var(--text-dim); border: 1px solid var(--border); } .btn-secondary:hover { color: var(--text); background: var(--surface); }
|
|
50
|
+
.response-panel { border-left: 1px solid var(--border); display: flex; flex-direction: column; overflow: hidden; background: #110e0a; }
|
|
127
51
|
.response-header { padding: 16px 20px; border-bottom: 1px solid var(--border); display: flex; align-items: center; background: var(--surface); height: 50px; }
|
|
128
52
|
.response-header-title { font-size: 11px; font-family: 'DM Mono', monospace; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.05em; }
|
|
129
53
|
.status-badge { font-family: 'DM Mono', monospace; font-size: 12px; margin-left: auto; padding: 2px 8px; border-radius: 4px; font-weight: 500; }
|
|
130
|
-
.status-ok { background: #1a3a22; color: #6ba05a; }
|
|
131
|
-
.
|
|
132
|
-
.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
.response-body {
|
|
136
|
-
flex: 1;
|
|
137
|
-
overflow-y: auto;
|
|
138
|
-
overflow-x: hidden;
|
|
139
|
-
padding: 0;
|
|
140
|
-
background: #0d0b08;
|
|
141
|
-
}
|
|
142
|
-
.response-body.empty {
|
|
143
|
-
color: var(--text-dim);
|
|
144
|
-
display: flex;
|
|
145
|
-
align-items: center;
|
|
146
|
-
justify-content: center;
|
|
147
|
-
padding: 20px;
|
|
148
|
-
text-align: center;
|
|
149
|
-
font-size: 13px;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/* FIXED: Token code output container handles micro horizontal data flows elegantly */
|
|
153
|
-
.json-render-block {
|
|
154
|
-
display: block;
|
|
155
|
-
padding: 20px;
|
|
156
|
-
margin: 0;
|
|
157
|
-
font-family: 'DM Mono', monospace;
|
|
158
|
-
font-size: 12px;
|
|
159
|
-
line-height: 1.5;
|
|
160
|
-
white-space: pre;
|
|
161
|
-
overflow-x: auto;
|
|
162
|
-
word-break: normal;
|
|
163
|
-
word-wrap: normal;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
.json-key { color: #e8a838; }
|
|
167
|
-
.json-str { color: #9ab878; }
|
|
168
|
-
.json-num { color: #5a86c0; }
|
|
169
|
-
|
|
54
|
+
.status-ok { background: #1a3a22; color: #6ba05a; } .status-err { background: #3a1a14; color: #d45c3c; } .status-idle { background: var(--surface2); color: var(--text-dim); }
|
|
55
|
+
.response-body { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 0; background: #0d0b08; }
|
|
56
|
+
.response-body.empty { color: var(--text-dim); display: flex; align-items: center; justify-content: center; padding: 20px; text-align: center; font-size: 13px; }
|
|
57
|
+
.json-render-block { display: block; padding: 20px; margin: 0; font-family: 'DM Mono', monospace; font-size: 12px; line-height: 1.5; white-space: pre; overflow-x: auto; word-break: normal; word-wrap: normal; }
|
|
58
|
+
.json-key { color: #e8a838; } .json-str { color: #9ab878; } .json-num { color: #5a86c0; } .json-bool { color: #c47a1e; } .json-null { color: var(--text-dim); }
|
|
170
59
|
#toast { position: fixed; bottom: 24px; right: 24px; background: var(--surface2); border: 1px solid var(--border); padding: 10px 18px; border-radius: var(--radius); opacity: 0; transition: all .25s; z-index: 1000; font-family: 'DM Mono', monospace; font-size: 12px; color: var(--accent); }
|
|
171
60
|
#toast.show { opacity: 1; }
|
|
61
|
+
.empty-state { text-align: center; padding: 60px 20px; color: var(--text-dim); }
|
|
62
|
+
.empty-state .monkey { font-size: 48px; margin-bottom: 16px; }
|
|
63
|
+
.empty-state h2 { color: var(--text); font-family: 'Playfair Display', serif; margin-bottom: 8px; }
|
|
172
64
|
</style>
|
|
173
65
|
</head>
|
|
174
66
|
<body>
|
|
175
67
|
|
|
176
|
-
<div id="
|
|
68
|
+
<div id="__monkey_data__" data-payload="${safeJsonString}" style="display:none;"></div>
|
|
177
69
|
|
|
178
70
|
<header>
|
|
179
|
-
<div class="logo"
|
|
71
|
+
<div class="logo">🐒 Endtester <span>Application Runtime Sandbox</span></div>
|
|
180
72
|
<div class="header-right">
|
|
181
73
|
<div class="base-url-wrap">
|
|
182
74
|
<label>TARGET HOST</label>
|
|
@@ -206,200 +98,182 @@ export function getHtmlTemplate(endpoints) {
|
|
|
206
98
|
<div id="toast"></div>
|
|
207
99
|
|
|
208
100
|
<script>
|
|
209
|
-
const
|
|
210
|
-
|
|
101
|
+
const ENDPOINTS = JSON.parse(atob(document.getElementById('__monkey_data__').getAttribute('data-payload')));
|
|
102
|
+
let currentKey = null;
|
|
211
103
|
|
|
212
|
-
|
|
104
|
+
document.getElementById('base-url').value = window.location.origin;
|
|
213
105
|
|
|
214
|
-
|
|
106
|
+
function buildSidebar() {
|
|
107
|
+
const sidebar = document.getElementById('sidebar-nav');
|
|
108
|
+
const keys = Object.keys(ENDPOINTS);
|
|
215
109
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
110
|
+
if (keys.length === 0) {
|
|
111
|
+
sidebar.innerHTML += '<div style="padding:18px;color:var(--text-dim);font-size:12px">No endpoints discovered.</div>';
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
keys.forEach((key, i) => {
|
|
116
|
+
const ep = ENDPOINTS[key];
|
|
117
|
+
const item = document.createElement('div');
|
|
118
|
+
item.className = 'nav-item' + (i === 0 ? ' active' : '');
|
|
119
|
+
item.setAttribute('data-key', key);
|
|
120
|
+
item.innerHTML =
|
|
121
|
+
'<span class="method-badge ' + ep.method + '">' + ep.method + '</span>' +
|
|
122
|
+
'<span class="nav-label">' + ep.path + '</span>';
|
|
123
|
+
item.addEventListener('click', () => {
|
|
124
|
+
document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
|
|
125
|
+
item.classList.add('active');
|
|
126
|
+
clearResponse();
|
|
127
|
+
renderPanel(key);
|
|
128
|
+
});
|
|
129
|
+
sidebar.appendChild(item);
|
|
130
|
+
});
|
|
219
131
|
|
|
220
|
-
|
|
221
|
-
sidebar.innerHTML += '<div style="padding:15px; color:var(--text-dim)">No active application endpoints discovered.</div>';
|
|
222
|
-
return;
|
|
132
|
+
renderPanel(keys[0]);
|
|
223
133
|
}
|
|
224
134
|
|
|
225
|
-
|
|
135
|
+
function renderPanel(key) {
|
|
136
|
+
currentKey = key;
|
|
226
137
|
const ep = ENDPOINTS[key];
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
138
|
+
const main = document.getElementById('main-panel');
|
|
139
|
+
if (!ep) return;
|
|
140
|
+
|
|
141
|
+
let html =
|
|
142
|
+
'<div class="endpoint-title">' + ep.title + '</div>' +
|
|
143
|
+
'<div class="endpoint-path"><span class="method-badge ' + ep.method + '">' + ep.method + '</span>' +
|
|
144
|
+
'<span>' + ep.path + '</span></div>' +
|
|
145
|
+
'<div class="endpoint-desc">' + ep.desc + '</div>';
|
|
146
|
+
|
|
147
|
+
if (ep.params && ep.params.length) {
|
|
148
|
+
html += '<div class="form-section"><div class="form-section-title">Path Parameters</div>';
|
|
149
|
+
ep.params.forEach(function(p) {
|
|
150
|
+
html +=
|
|
151
|
+
'<div class="field-row">' +
|
|
152
|
+
'<label class="field-label">' + p.label + '</label>' +
|
|
153
|
+
'<input type="text" id="param-' + p.name + '" placeholder="' + p.placeholder + '" />' +
|
|
154
|
+
'</div>';
|
|
155
|
+
});
|
|
156
|
+
html += '</div>';
|
|
157
|
+
}
|
|
242
158
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
159
|
+
if (ep.fields && ep.fields.length) {
|
|
160
|
+
html += '<div class="form-section"><div class="form-section-title">HTTP JSON Request Payload Parameters</div>';
|
|
161
|
+
ep.fields.forEach(function(f) {
|
|
162
|
+
html +=
|
|
163
|
+
'<div class="field-row">' +
|
|
164
|
+
'<label class="field-label">' + f.label + '</label>' +
|
|
165
|
+
'<input type="' + (f.type || 'text') + '" id="field-' + f.name + '" placeholder="' + (f.placeholder || '') + '" />' +
|
|
166
|
+
'</div>';
|
|
167
|
+
});
|
|
168
|
+
html += '</div>';
|
|
169
|
+
}
|
|
247
170
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
let html = \`
|
|
255
|
-
<div class="endpoint-title">\${ep.title}</div>
|
|
256
|
-
<div class="endpoint-path">
|
|
257
|
-
<span class="method-badge \${ep.method}">\${ep.method}</span>
|
|
258
|
-
<span>\${ep.path}</span>
|
|
259
|
-
</div>
|
|
260
|
-
<div class="endpoint-desc">\${ep.desc}</div>
|
|
261
|
-
\`;
|
|
262
|
-
|
|
263
|
-
if (ep.params && ep.params.length) {
|
|
264
|
-
html += \`<div class="form-section"><div class="form-section-title">Path Parameters</div>\`;
|
|
265
|
-
ep.params.forEach(function(p) {
|
|
266
|
-
const inputHtml = makeInputString('text', 'param-' + p.name, p.placeholder);
|
|
267
|
-
html += \`
|
|
268
|
-
<div class="field-row">
|
|
269
|
-
<label class="field-label">\${p.label}</label>
|
|
270
|
-
\${inputHtml}
|
|
271
|
-
</div>
|
|
272
|
-
\`;
|
|
273
|
-
});
|
|
274
|
-
html += \`</div>\`;
|
|
275
|
-
}
|
|
171
|
+
html +=
|
|
172
|
+
'<div class="btn-row">' +
|
|
173
|
+
'<button class="btn" onclick="sendRequest()">Execute Route</button>' +
|
|
174
|
+
'<button class="btn btn-secondary" onclick="clearResponse()">Clear Context</button>' +
|
|
175
|
+
'</div>';
|
|
276
176
|
|
|
277
|
-
|
|
278
|
-
html += \`<div class="form-section"><div class="form-section-title">HTTP JSON Request Payload Parameters</div>\`;
|
|
279
|
-
ep.fields.forEach(function(f) {
|
|
280
|
-
const inputHtml = makeInputString(f.type || 'text', 'field-' + f.name, f.placeholder || '');
|
|
281
|
-
html += \`
|
|
282
|
-
<div class="field-row">
|
|
283
|
-
<label class="field-label">\${f.label}</label>
|
|
284
|
-
\${inputHtml}
|
|
285
|
-
</div>
|
|
286
|
-
\`;
|
|
287
|
-
});
|
|
288
|
-
html += \`</div>\`;
|
|
177
|
+
main.innerHTML = html;
|
|
289
178
|
}
|
|
290
179
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
<button class="btn btn-secondary" onclick="clearResponse()">Clear Context</button>
|
|
295
|
-
</div>
|
|
296
|
-
\`;
|
|
180
|
+
async function sendRequest() {
|
|
181
|
+
const ep = ENDPOINTS[currentKey];
|
|
182
|
+
let path = ep.path;
|
|
297
183
|
|
|
298
|
-
|
|
299
|
-
|
|
184
|
+
if (ep.params && ep.params.length) {
|
|
185
|
+
for (const p of ep.params) {
|
|
186
|
+
const val = (document.getElementById('param-' + p.name) || {}).value || '';
|
|
187
|
+
if (!val.trim()) { showToast('⚠ Path param "' + p.label + '" is required'); return; }
|
|
188
|
+
path = path.replace(':' + p.name, encodeURIComponent(val.trim()));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
300
191
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
192
|
+
const baseUrl = document.getElementById('base-url').value.replace(/\/+$/, '');
|
|
193
|
+
const url = baseUrl + path;
|
|
194
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
195
|
+
const jwt = document.getElementById('jwt-input').value.trim();
|
|
196
|
+
if (jwt) headers['Authorization'] = 'Bearer ' + jwt;
|
|
197
|
+
|
|
198
|
+
let body = undefined;
|
|
199
|
+
if (['POST', 'PUT', 'PATCH'].includes(ep.method) && ep.fields && ep.fields.length) {
|
|
200
|
+
const payload = {};
|
|
201
|
+
ep.fields.forEach(function(f) {
|
|
202
|
+
const el = document.getElementById('field-' + f.name);
|
|
203
|
+
if (!el) return;
|
|
204
|
+
let v = el.value.trim();
|
|
205
|
+
if (f.type === 'number' && v !== '') v = Number(v);
|
|
206
|
+
payload[f.name] = v;
|
|
207
|
+
});
|
|
208
|
+
body = JSON.stringify(payload);
|
|
209
|
+
}
|
|
304
210
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
211
|
+
setResponse(null, 'loading');
|
|
212
|
+
const t0 = Date.now();
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const res = await fetch(url, { method: ep.method, headers, body });
|
|
216
|
+
const ms = Date.now() - t0;
|
|
217
|
+
const text = await res.text();
|
|
218
|
+
let data;
|
|
219
|
+
try { data = JSON.parse(text); } catch { data = text; }
|
|
220
|
+
setResponse(data, res.ok ? 'ok' : 'err', res.status, ms);
|
|
221
|
+
} catch (err) {
|
|
222
|
+
setResponse({ error: err.message }, 'err', 'FAIL', 0);
|
|
310
223
|
}
|
|
311
224
|
}
|
|
312
225
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
for (const f of ep.fields) {
|
|
324
|
-
const targetEl = document.getElementById('field-' + f.name);
|
|
325
|
-
if (targetEl) {
|
|
326
|
-
let entryVal = targetEl.value.trim();
|
|
327
|
-
if (f.type === 'number' && entryVal !== '') {
|
|
328
|
-
entryVal = Number(entryVal);
|
|
329
|
-
}
|
|
330
|
-
jsonPayload[f.name] = entryVal;
|
|
331
|
-
}
|
|
226
|
+
function setResponse(data, state, status, ms) {
|
|
227
|
+
const badge = document.getElementById('status-badge');
|
|
228
|
+
const body = document.getElementById('response-body');
|
|
229
|
+
|
|
230
|
+
if (state === 'loading') {
|
|
231
|
+
badge.className = 'status-badge status-idle';
|
|
232
|
+
badge.textContent = '…';
|
|
233
|
+
body.className = 'response-body empty';
|
|
234
|
+
body.textContent = 'Executing transmission…';
|
|
235
|
+
return;
|
|
332
236
|
}
|
|
333
|
-
body = JSON.stringify(jsonPayload);
|
|
334
|
-
}
|
|
335
237
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
const ms = Date.now() - start;
|
|
342
|
-
const text = await res.text();
|
|
343
|
-
let json;
|
|
344
|
-
try { json = JSON.parse(text); } catch { json = text; }
|
|
345
|
-
setResponse(json, res.ok ? 'ok' : 'err', res.status, ms);
|
|
346
|
-
} catch (err) {
|
|
347
|
-
setResponse({ error: err.message }, 'err', 'FAIL', 0);
|
|
238
|
+
badge.className = 'status-badge ' + (state === 'ok' ? 'status-ok' : 'status-err');
|
|
239
|
+
badge.textContent = status + ' · ' + ms + 'ms';
|
|
240
|
+
body.className = 'response-body';
|
|
241
|
+
const str = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
|
|
242
|
+
body.innerHTML = '<pre class="json-render-block">' + highlight(str) + '</pre>';
|
|
348
243
|
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
function setResponse(data, state, status, ms) {
|
|
352
|
-
const badge = document.getElementById('status-badge');
|
|
353
|
-
const body = document.getElementById('response-body');
|
|
354
244
|
|
|
355
|
-
|
|
356
|
-
badge.className = 'status-badge status-idle';
|
|
357
|
-
badge.textContent = '
|
|
245
|
+
function clearResponse() {
|
|
246
|
+
document.getElementById('status-badge').className = 'status-badge status-idle';
|
|
247
|
+
document.getElementById('status-badge').textContent = '—';
|
|
248
|
+
const body = document.getElementById('response-body');
|
|
358
249
|
body.className = 'response-body empty';
|
|
359
|
-
body.
|
|
360
|
-
return;
|
|
250
|
+
body.textContent = 'Execute a request row to generate feedback data';
|
|
361
251
|
}
|
|
362
252
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
badge.className = 'status-badge status-idle';
|
|
376
|
-
badge.textContent = '—';
|
|
377
|
-
body.className = 'response-body empty';
|
|
378
|
-
body.textContent = 'Execute a request row to generate feedback data';
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
function highlightJson(str) {
|
|
382
|
-
return str
|
|
383
|
-
.replace(/&/g, '&').replace(/[<]/g, '<').replace(/[>]/g, '>')
|
|
384
|
-
.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function(match) {
|
|
385
|
-
if (/^"/.test(match)) {
|
|
386
|
-
if (/:$/.test(match)) return '<span class="json-key">' + match + '</span>';
|
|
387
|
-
return '<span class="json-str">' + match + '</span>';
|
|
388
|
-
}
|
|
389
|
-
return '<span class="json-num">' + match + '</span>';
|
|
390
|
-
});
|
|
391
|
-
}
|
|
253
|
+
function highlight(str) {
|
|
254
|
+
return str
|
|
255
|
+
.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')
|
|
256
|
+
.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false)\b|\bnull\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function(m) {
|
|
257
|
+
if (/^"/.test(m)) return /:$/.test(m)
|
|
258
|
+
? '<span class="json-key">' + m + '</span>'
|
|
259
|
+
: '<span class="json-str">' + m + '</span>';
|
|
260
|
+
if (/true|false/.test(m)) return '<span class="json-bool">' + m + '</span>';
|
|
261
|
+
if (/null/.test(m)) return '<span class="json-null">' + m + '</span>';
|
|
262
|
+
return '<span class="json-num">' + m + '</span>';
|
|
263
|
+
});
|
|
264
|
+
}
|
|
392
265
|
|
|
393
|
-
function showToast(msg) {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
}
|
|
266
|
+
function showToast(msg) {
|
|
267
|
+
const t = document.getElementById('toast');
|
|
268
|
+
t.textContent = msg;
|
|
269
|
+
t.classList.add('show');
|
|
270
|
+
setTimeout(() => t.classList.remove('show'), 2500);
|
|
271
|
+
}
|
|
399
272
|
|
|
400
|
-
buildSidebar();
|
|
273
|
+
buildSidebar();
|
|
401
274
|
</script>
|
|
402
275
|
</body>
|
|
403
|
-
</html
|
|
404
|
-
`;
|
|
276
|
+
</html>`;
|
|
405
277
|
}
|
|
278
|
+
|
|
279
|
+
exports = { getHtmlTemplate };
|
package/monkey.js
CHANGED