@aimeloic/monkey-tester 1.0.10 → 2.0.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/html.backup.js ADDED
@@ -0,0 +1,317 @@
1
+ export function getHtmlTemplate(endpoints) {
2
+ const safeJsonString = Buffer.from(JSON.stringify(endpoints)).toString('base64');
3
+
4
+ return `
5
+ <!DOCTYPE html>
6
+ <html lang="en">
7
+ <head>
8
+ <meta charset="UTF-8">
9
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
10
+ <title>Endtester — API Environment</title>
11
+ <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
+ <style>
13
+ :root {
14
+ --bg: #0e0c09;
15
+ --surface: #181510;
16
+ --surface2: #221d14;
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;
26
+ }
27
+ * { box-sizing: border-box; margin: 0; padding: 0; }
28
+ body {
29
+ background: var(--bg); color: var(--text); font-family: 'DM Sans', sans-serif; font-size: 14px; min-height: 100vh;
30
+ background-image: radial-gradient(ellipse 80% 60% at 50% -20%, #3a2a0a22 0%, transparent 70%);
31
+ }
32
+ header {
33
+ border-bottom: 1px solid var(--border); padding: 20px 32px; display: flex; align-items: center; gap: 20px;
34
+ background: #0e0c09ee; backdrop-filter: blur(8px); position: sticky; top: 0; z-index: 100;
35
+ }
36
+ .logo { font-family: 'Playfair Display', serif; font-size: 22px; color: var(--accent); letter-spacing: 0.02em; }
37
+ .logo span { color: var(--text-dim); font-size: 13px; font-family: 'DM Mono', monospace; display: block; font-weight: 400; }
38
+ .header-right { margin-left: auto; display: flex; align-items: center; gap: 12px; }
39
+ .jwt-wrap, .base-url-wrap { display: flex; align-items: center; gap: 8px; }
40
+ .jwt-wrap label, .base-url-wrap label { color: var(--text-dim); font-size: 12px; font-family: 'DM Mono', monospace; }
41
+ #jwt-input, #base-url {
42
+ background: var(--surface2); border: 1px solid var(--border); color: var(--text);
43
+ font-family: 'DM Mono', monospace; font-size: 11px; padding: 6px 10px; border-radius: var(--radius); width: 220px; outline: none;
44
+ }
45
+ .layout { display: grid; grid-template-columns: 260px 1fr 400px; height: calc(100vh - 65px); }
46
+ aside { border-right: 1px solid var(--border); overflow-y: auto; padding: 16px 0; }
47
+ .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; }
48
+ .nav-item { display: flex; align-items: center; gap: 10px; padding: 8px 18px; cursor: pointer; border-left: 2px solid transparent; color: var(--text-dim); }
49
+ .nav-item:hover { background: var(--surface); color: var(--text); }
50
+ .nav-item.active { border-left-color: var(--accent); background: var(--surface); color: var(--accent); }
51
+ .method-badge { font-family: 'DM Mono', monospace; font-size: 9px; font-weight: 500; padding: 2px 5px; border-radius: 3px; min-width: 45px; text-align: center; }
52
+ .GET { background: #1a3a22; color: #6ba05a; }
53
+ .POST { background: #1a2e3a; color: #5a86c0; }
54
+ .PUT, .PATCH { background: #3a2e10; color: #e8a838; }
55
+ .DELETE { background: #3a1a14; color: #d45c3c; }
56
+ .nav-label { font-size: 11px; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
57
+ main { overflow-y: auto; padding: 24px; }
58
+ .endpoint-title { font-family: 'Playfair Display', serif; font-size: 20px; color: var(--accent); margin-bottom: 6px; }
59
+ .endpoint-path { font-family: 'DM Mono', monospace; font-size: 12px; color: var(--text-dim); margin-bottom: 20px; display: flex; align-items: center; gap: 8px; }
60
+ .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; }
61
+ .form-section { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px; margin-bottom: 16px; }
62
+ .form-section-title { font-size: 11px; font-family: 'DM Mono', monospace; color: var(--text-dim); letter-spacing: 0.1em; text-transform: uppercase; margin-bottom: 14px; }
63
+ .field-row { display: grid; grid-template-columns: 140px 1fr; align-items: center; gap: 10px; margin-bottom: 10px; }
64
+ .field-label { font-family: 'DM Mono', monospace; font-size: 11px; color: var(--text-dim); text-align: right; }
65
+ input[type=text], input[type=number], select { background: var(--surface2); border: 1px solid var(--border); color: var(--text); font-family: 'DM Mono', monospace; font-size: 12px; padding: 7px 10px; border-radius: var(--radius); width: 100%; outline: none; }
66
+ .btn { background: var(--accent); color: #0e0c09; border: none; padding: 10px 22px; border-radius: var(--radius); font-size: 13px; font-weight: 500; cursor: pointer; }
67
+ .btn-row { margin-top: 20px; display: flex; gap: 10px; }
68
+ .btn-secondary { background: var(--surface2); color: var(--text-dim); border: 1px solid var(--border); }
69
+ .response-panel { border-left: 1px solid var(--border); display: flex; flex-direction: column; overflow: hidden; }
70
+ .response-header { padding: 14px 18px; border-bottom: 1px solid var(--border); display: flex; align-items: center; background: var(--surface); }
71
+ .response-header-title { font-size: 11px; font-family: 'DM Mono', monospace; color: var(--text-dim); text-transform: uppercase; }
72
+ .status-badge { font-family: 'DM Mono', monospace; font-size: 12px; margin-left: auto; padding: 2px 8px; border-radius: 4px; }
73
+ .status-ok { background: #1a3a22; color: #6ba05a; }
74
+ .status-err { background: #3a1a14; color: #d45c3c; }
75
+ .status-idle { background: var(--surface2); color: var(--text-dim); }
76
+ .response-body { flex: 1; overflow-y: auto; padding: 16px; font-family: 'DM Mono', monospace; font-size: 12px; white-space: pre-wrap; word-break: break-all; }
77
+ .response-body.empty { color: var(--text-dim); display: flex; align-items: center; justify-content: center; }
78
+ .json-key { color: #e8a838; } .json-str { color: #9ab878; } .json-num { color: #5a86c0; }
79
+ #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; }
80
+ #toast.show { opacity: 1; }
81
+ </style>
82
+ </head>
83
+ <body>
84
+
85
+ <div id="endtester-data-vault" data-payload="${safeJsonString}" style="display: none;"></div>
86
+
87
+ <header>
88
+ <div class="logo">Endtester <span>Application Runtime Sandbox</span></div>
89
+ <div class="header-right">
90
+ <div class="base-url-wrap">
91
+ <label>TARGET HOST</label>
92
+ <input id="base-url" type="text" value="">
93
+ </div>
94
+ <div class="jwt-wrap">
95
+ <label>BEARER AUTH</label>
96
+ <input id="jwt-input" type="text" placeholder="Token value...">
97
+ </div>
98
+ </div>
99
+ </header>
100
+
101
+ <div class="layout">
102
+ <aside id="sidebar-nav">
103
+ <div class="section-label">Discovered Endpoints</div>
104
+ </aside>
105
+ <main id="main-panel"></main>
106
+ <div class="response-panel">
107
+ <div class="response-header">
108
+ <span class="response-header-title">Response Output</span>
109
+ <span id="status-badge" class="status-badge status-idle">—</span>
110
+ </div>
111
+ <div class="response-body empty" id="response-body">Execute a request row to generate feedback data</div>
112
+ </div>
113
+ </div>
114
+
115
+ <div id="toast"></div>
116
+
117
+ <script>
118
+ const rawPayload = document.getElementById('endtester-data-vault').getAttribute('data-payload');
119
+ const ENDPOINTS = JSON.parse(atob(rawPayload));
120
+
121
+ let currentEp = '';
122
+
123
+ document.getElementById('base-url').value = window.location.origin;
124
+
125
+ function buildSidebar() {
126
+ const sidebar = document.getElementById('sidebar-nav');
127
+ const keys = Object.keys(ENDPOINTS);
128
+
129
+ if (keys.length === 0) {
130
+ sidebar.innerHTML += '<div style="padding:15px; color:var(--text-dim)">No active application endpoints discovered.</div>';
131
+ return;
132
+ }
133
+
134
+ keys.forEach((key, index) => {
135
+ const ep = ENDPOINTS[key];
136
+ const div = document.createElement('div');
137
+ div.className = index === 0 ? 'nav-item active' : 'nav-item';
138
+ div.setAttribute('data-ep', key);
139
+ div.innerHTML = '<span class="method-badge ' + ep.method + '">' + ep.method + '</span><span class="nav-label">' + ep.path + '</span>';
140
+ div.addEventListener('click', () => {
141
+ document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
142
+ div.classList.add('active');
143
+ clearResponse();
144
+ renderPanel(key);
145
+ });
146
+ sidebar.appendChild(div);
147
+ });
148
+
149
+ if (keys.length > 0) renderPanel(keys[0]);
150
+ }
151
+
152
+ function makeInputString(type, id, placeholder, defaultValue) {
153
+ const pAttr = placeholder ? ' placeholder="' + placeholder + '"' : '';
154
+ const vAttr = defaultValue !== undefined ? ' value="' + defaultValue + '"' : '';
155
+ return '<input type="' + type + '" id="' + id + '"' + pAttr + vAttr + ' />';
156
+ }
157
+
158
+ function renderPanel(epKey) {
159
+ currentEp = epKey;
160
+ const ep = ENDPOINTS[epKey];
161
+ const main = document.getElementById('main-panel');
162
+ if (!ep) return;
163
+
164
+ let html = \`
165
+ <div class="endpoint-title">\${ep.title}</div>
166
+ <div class="endpoint-path">
167
+ <span class="method-badge \${ep.method}">\${ep.method}</span>
168
+ <span>\${ep.path}</span>
169
+ </div>
170
+ <div class="endpoint-desc">\${ep.desc}</div>
171
+ \`;
172
+
173
+ // Path Parameters
174
+ if (ep.params && ep.params.length) {
175
+ html += \`<div class="form-section"><div class="form-section-title">Path Parameters</div>\`;
176
+ ep.params.forEach(function(p) {
177
+ const inputHtml = makeInputString('text', 'param-' + p.name, p.placeholder, '');
178
+ html += \`
179
+ <div class="field-row">
180
+ <label class="field-label">\${p.label}</label>
181
+ \${inputHtml}
182
+ </div>
183
+ \`;
184
+ });
185
+ html += \`</div>\`;
186
+ }
187
+
188
+ // FIXED: Renders native, individual form fields instead of a single text payload editor block
189
+ if (ep.fields && ep.fields.length) {
190
+ html += \`<div class="form-section"><div class="form-section-title">HTTP JSON Request Payload Parameters</div>\`;
191
+ ep.fields.forEach(function(f) {
192
+ const inputHtml = makeInputString(f.type || 'text', 'field-' + f.name, f.placeholder || '', '');
193
+ html += \`
194
+ <div class="field-row">
195
+ <label class="field-label">\--\${f.label}</label>
196
+ \${inputHtml}
197
+ </div>
198
+ \`;
199
+ });
200
+ html += \`</div>\`;
201
+ }
202
+
203
+ html += \`
204
+ <div class="btn-row">
205
+ <button class="btn" onclick="sendRequest()">Execute Route</button>
206
+ <button class="btn btn-secondary" onclick="clearResponse()">Clear Context</button>
207
+ </div>
208
+ \`;
209
+
210
+ main.innerHTML = html;
211
+ }
212
+
213
+ async function sendRequest() {
214
+ const ep = ENDPOINTS[currentEp];
215
+ let path = ep.path;
216
+
217
+ // Compile URL path parameter tags
218
+ if (ep.params) {
219
+ for (const p of ep.params) {
220
+ const val = document.getElementById('param-' + p.name)?.value.trim();
221
+ if (!val) { showToast('Warning: Parameter ' + p.label + ' is required'); return; }
222
+ path = path.replace(':' + p.name, encodeURIComponent(val));
223
+ }
224
+ }
225
+
226
+ const baseUrl = document.getElementById('base-url').value.replace(/[/]+$/, '');
227
+ const url = baseUrl + path;
228
+ const headers = { 'Content-Type': 'application/json' };
229
+
230
+ const jwt = document.getElementById('jwt-input').value.trim();
231
+ if (jwt) headers['Authorization'] = 'Bearer ' + jwt;
232
+
233
+ // FIXED: Dynamically bundles inputs into a background payload object structure seamlessly
234
+ let body = undefined;
235
+ if (ep.fields && ep.fields.length && ['POST', 'PUT', 'PATCH'].includes(ep.method)) {
236
+ const payloadObject = {};
237
+
238
+ for (const f of ep.fields) {
239
+ const inputEl = document.getElementById('field-' + f.name);
240
+ if (inputEl) {
241
+ let value = inputEl.value.trim();
242
+ // Cast numerical parameters to prevent type validation parsing failures
243
+ if (f.type === 'number' && value !== '') {
244
+ value = Number(value);
245
+ }
246
+ payloadObject[f.name] = value;
247
+ }
248
+ }
249
+ body = JSON.stringify(payloadObject);
250
+ }
251
+
252
+ setResponse(null, 'loading');
253
+ const start = Date.now();
254
+
255
+ try {
256
+ const res = await fetch(url, { method: ep.method, headers, body });
257
+ const ms = Date.now() - start;
258
+ const text = await res.text();
259
+ let json;
260
+ try { json = JSON.parse(text); } catch { json = text; }
261
+ setResponse(json, res.ok ? 'ok' : 'err', res.status, ms);
262
+ } catch (err) {
263
+ setResponse({ error: err.message }, 'err', 'FAIL', 0);
264
+ }
265
+ }
266
+
267
+ function setResponse(data, state, status, ms) {
268
+ const badge = document.getElementById('status-badge');
269
+ const body = document.getElementById('response-body');
270
+
271
+ if (state === 'loading') {
272
+ badge.className = 'status-badge status-idle';
273
+ badge.textContent = '...';
274
+ body.innerHTML = 'Executing transmission...';
275
+ return;
276
+ }
277
+
278
+ badge.className = 'status-badge ' + (state === 'ok' ? 'status-ok' : 'status-err');
279
+ badge.textContent = status + ' · ' + ms + 'ms';
280
+ body.className = 'response-body';
281
+ body.innerHTML = highlightJson(typeof data === 'string' ? data : JSON.stringify(data, null, 2));
282
+ }
283
+
284
+ function clearResponse() {
285
+ const badge = document.getElementById('status-badge');
286
+ const body = document.getElementById('response-body');
287
+ badge.className = 'status-badge status-idle';
288
+ badge.textContent = '—';
289
+ body.className = 'response-body empty';
290
+ body.textContent = 'Execute a request row to generate feedback data';
291
+ }
292
+
293
+ function highlightJson(str) {
294
+ return str
295
+ .replace(/&/g, '&amp;').replace(/[<]/g, '&lt;').replace(/[>]/g, '&gt;')
296
+ .replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function(match) {
297
+ if (/^"/.test(match)) {
298
+ if (/:$/.test(match)) return '<span class="json-key">' + match + '</span>';
299
+ return '<span class="json-str">' + match + '</span>';
300
+ }
301
+ return '<span class="json-num">' + match + '</span>';
302
+ });
303
+ }
304
+
305
+ function showToast(msg) {
306
+ const t = document.getElementById('toast');
307
+ t.textContent = msg;
308
+ t.classList.add('show');
309
+ setTimeout(() => t.classList.remove('show'), 2500);
310
+ }
311
+
312
+ buildSidebar();
313
+ </script>
314
+ </body>
315
+ </html>
316
+ `;
317
+ }