@aimeloic/monkey-tester 2.0.2 → 2.0.4

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 CHANGED
@@ -24,59 +24,150 @@ export function getHtmlTemplate(endpoints) {
24
24
  --blue: #5a86c0;
25
25
  --radius: 8px;
26
26
  }
27
+
27
28
  * { box-sizing: border-box; margin: 0; padding: 0; }
29
+
28
30
  body {
29
- background: var(--bg); color: var(--text); font-family: 'DM Sans', sans-serif; font-size: 14px; min-height: 100vh;
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 */
30
37
  background-image: radial-gradient(ellipse 80% 60% at 50% -20%, #3a2a0a22 0%, transparent 70%);
31
38
  }
39
+
32
40
  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;
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;
35
49
  }
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; }
50
+
51
+ .logo { font-family: 'Playfair Display', serif; font-size: 20px; color: var(--accent); letter-spacing: 0.02em; }
52
+ .logo span { color: var(--text-dim); font-size: 11px; font-family: 'DM Mono', monospace; display: inline-block; margin-left: 8px; font-weight: 400; }
53
+ .header-right { margin-left: auto; display: flex; align-items: center; gap: 16px; }
39
54
  .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; }
55
+ .jwt-wrap label, .base-url-wrap label { color: var(--text-dim); font-size: 11px; font-family: 'DM Mono', monospace; letter-spacing: 0.05em; }
56
+
41
57
  #jwt-input, #base-url {
42
58
  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;
59
+ font-family: 'DM Mono', monospace; font-size: 12px; padding: 6px 12px; border-radius: var(--radius); width: 220px; outline: none;
44
60
  }
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; }
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
+
47
77
  .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); }
78
+
79
+ .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; }
49
80
  .nav-item:hover { background: var(--surface); color: var(--text); }
50
81
  .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; }
82
+
83
+ .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; }
52
84
  .GET { background: #1a3a22; color: #6ba05a; }
53
85
  .POST { background: #1a2e3a; color: #5a86c0; }
54
86
  .PUT, .PATCH { background: #3a2e10; color: #e8a838; }
55
87
  .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; }
88
+
89
+ .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
+
97
+ .endpoint-title { font-family: 'Playfair Display', serif; font-size: 24px; color: var(--accent); margin-bottom: 8px; }
98
+ .endpoint-path { font-family: 'DM Mono', monospace; font-size: 13px; color: var(--text-dim); margin-bottom: 24px; display: flex; align-items: center; gap: 8px; }
60
99
  .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; }
100
+
101
+ .form-section { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px; margin-bottom: 20px; }
102
+ .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
+ .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
+ .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
+ }
111
+ input:focus { border-color: var(--accent); }
112
+
113
+ .btn-row { margin-top: 24px; display: flex; gap: 12px; }
114
+ .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; }
68
116
  .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; }
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
+ }
127
+ .response-header { padding: 16px 20px; border-bottom: 1px solid var(--border); display: flex; align-items: center; background: var(--surface); height: 50px; }
128
+ .response-header-title { font-size: 11px; font-family: 'DM Mono', monospace; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.05em; }
129
+ .status-badge { font-family: 'DM Mono', monospace; font-size: 12px; margin-left: auto; padding: 2px 8px; border-radius: 4px; font-weight: 500; }
73
130
  .status-ok { background: #1a3a22; color: #6ba05a; }
74
131
  .status-err { background: #3a1a14; color: #d45c3c; }
75
132
  .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; }
133
+
134
+ /* FIXED: Body panel scrolls y natively, inner token element handles x scrolling */
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
+
170
+ #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); }
80
171
  #toast.show { opacity: 1; }
81
172
  </style>
82
173
  </head>
@@ -149,10 +240,9 @@ function buildSidebar() {
149
240
  if (keys.length > 0) renderPanel(keys[0]);
150
241
  }
151
242
 
152
- function makeInputString(type, id, placeholder, defaultValue) {
243
+ function makeInputString(type, id, placeholder) {
153
244
  const pAttr = placeholder ? ' placeholder="' + placeholder + '"' : '';
154
- const vAttr = defaultValue !== undefined ? ' value="' + defaultValue + '"' : '';
155
- return '<input type="' + type + '" id="' + id + '"' + pAttr + vAttr + ' />';
245
+ return '<input type="' + type + '" id="' + id + '"' + pAttr + ' />';
156
246
  }
157
247
 
158
248
  function renderPanel(epKey) {
@@ -170,11 +260,10 @@ function renderPanel(epKey) {
170
260
  <div class="endpoint-desc">\${ep.desc}</div>
171
261
  \`;
172
262
 
173
- // Path Parameters
174
263
  if (ep.params && ep.params.length) {
175
264
  html += \`<div class="form-section"><div class="form-section-title">Path Parameters</div>\`;
176
265
  ep.params.forEach(function(p) {
177
- const inputHtml = makeInputString('text', 'param-' + p.name, p.placeholder, '');
266
+ const inputHtml = makeInputString('text', 'param-' + p.name, p.placeholder);
178
267
  html += \`
179
268
  <div class="field-row">
180
269
  <label class="field-label">\${p.label}</label>
@@ -185,14 +274,13 @@ function renderPanel(epKey) {
185
274
  html += \`</div>\`;
186
275
  }
187
276
 
188
- // FIXED: Renders native, individual form fields instead of a single text payload editor block
189
277
  if (ep.fields && ep.fields.length) {
190
278
  html += \`<div class="form-section"><div class="form-section-title">HTTP JSON Request Payload Parameters</div>\`;
191
279
  ep.fields.forEach(function(f) {
192
- const inputHtml = makeInputString(f.type || 'text', 'field-' + f.name, f.placeholder || '', '');
280
+ const inputHtml = makeInputString(f.type || 'text', 'field-' + f.name, f.placeholder || '');
193
281
  html += \`
194
282
  <div class="field-row">
195
- <label class="field-label">\--\${f.label}</label>
283
+ <label class="field-label">\${f.label}</label>
196
284
  \${inputHtml}
197
285
  </div>
198
286
  \`;
@@ -214,7 +302,6 @@ async function sendRequest() {
214
302
  const ep = ENDPOINTS[currentEp];
215
303
  let path = ep.path;
216
304
 
217
- // Compile URL path parameter tags
218
305
  if (ep.params) {
219
306
  for (const p of ep.params) {
220
307
  const val = document.getElementById('param-' + p.name)?.value.trim();
@@ -230,23 +317,20 @@ async function sendRequest() {
230
317
  const jwt = document.getElementById('jwt-input').value.trim();
231
318
  if (jwt) headers['Authorization'] = 'Bearer ' + jwt;
232
319
 
233
- // FIXED: Dynamically bundles inputs into a background payload object structure seamlessly
234
320
  let body = undefined;
235
321
  if (ep.fields && ep.fields.length && ['POST', 'PUT', 'PATCH'].includes(ep.method)) {
236
- const payloadObject = {};
237
-
322
+ const jsonPayload = {};
238
323
  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);
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);
245
329
  }
246
- payloadObject[f.name] = value;
330
+ jsonPayload[f.name] = entryVal;
247
331
  }
248
332
  }
249
- body = JSON.stringify(payloadObject);
333
+ body = JSON.stringify(jsonPayload);
250
334
  }
251
335
 
252
336
  setResponse(null, 'loading');
@@ -271,6 +355,7 @@ function setResponse(data, state, status, ms) {
271
355
  if (state === 'loading') {
272
356
  badge.className = 'status-badge status-idle';
273
357
  badge.textContent = '...';
358
+ body.className = 'response-body empty';
274
359
  body.innerHTML = 'Executing transmission...';
275
360
  return;
276
361
  }
@@ -278,7 +363,10 @@ function setResponse(data, state, status, ms) {
278
363
  badge.className = 'status-badge ' + (state === 'ok' ? 'status-ok' : 'status-err');
279
364
  badge.textContent = status + ' · ' + ms + 'ms';
280
365
  body.className = 'response-body';
281
- body.innerHTML = highlightJson(typeof data === 'string' ? data : JSON.stringify(data, null, 2));
366
+
367
+ // FIXED: Nested rendering inside dedicated horizontal scrolling code tag block wrapper elements
368
+ const outputString = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
369
+ body.innerHTML = '<pre class="json-render-block">' + highlightJson(outputString) + '</pre>';
282
370
  }
283
371
 
284
372
  function clearResponse() {
package/htmlTemplate.js CHANGED
@@ -33,7 +33,7 @@ export function getHtmlTemplate(endpoints) {
33
33
  font-family: 'DM Sans', sans-serif;
34
34
  font-size: 14px;
35
35
  height: 100vh;
36
- overflow: hidden; /* Prevents whole-page scrolling */
36
+ overflow: hidden;
37
37
  background-image: radial-gradient(ellipse 80% 60% at 50% -20%, #3a2a0a22 0%, transparent 70%);
38
38
  }
39
39
 
@@ -59,7 +59,6 @@ export function getHtmlTemplate(endpoints) {
59
59
  font-family: 'DM Mono', monospace; font-size: 12px; padding: 6px 12px; border-radius: var(--radius); width: 220px; outline: none;
60
60
  }
61
61
 
62
- /* Fixed view height viewport matrix layout grid rules */
63
62
  .layout {
64
63
  display: grid;
65
64
  grid-template-columns: 280px 1fr 450px;
@@ -116,7 +115,6 @@ export function getHtmlTemplate(endpoints) {
116
115
  .btn-secondary { background: var(--surface2); color: var(--text-dim); border: 1px solid var(--border); }
117
116
  .btn-secondary:hover { color: var(--text); background: var(--surface); }
118
117
 
119
- /* FIXED: Response block scroll logic layout rules */
120
118
  .response-panel {
121
119
  border-left: 1px solid var(--border);
122
120
  display: flex;
@@ -131,7 +129,6 @@ export function getHtmlTemplate(endpoints) {
131
129
  .status-err { background: #3a1a14; color: #d45c3c; }
132
130
  .status-idle { background: var(--surface2); color: var(--text-dim); }
133
131
 
134
- /* FIXED: Body panel scrolls y natively, inner token element handles x scrolling */
135
132
  .response-body {
136
133
  flex: 1;
137
134
  overflow-y: auto;
@@ -149,7 +146,6 @@ export function getHtmlTemplate(endpoints) {
149
146
  font-size: 13px;
150
147
  }
151
148
 
152
- /* FIXED: Token code output container handles micro horizontal data flows elegantly */
153
149
  .json-render-block {
154
150
  display: block;
155
151
  padding: 20px;
@@ -240,34 +236,30 @@ function buildSidebar() {
240
236
  if (keys.length > 0) renderPanel(keys[0]);
241
237
  }
242
238
 
243
- function makeInputString(type, id, placeholder) {
244
- const pAttr = placeholder ? ' placeholder="' + placeholder + '"' : '';
245
- return '<input type="' + type + '" id="' + id + '"' + pAttr + ' />';
246
- }
247
-
248
239
  function renderPanel(epKey) {
249
240
  currentEp = epKey;
250
241
  const ep = ENDPOINTS[epKey];
251
242
  const main = document.getElementById('main-panel');
252
243
  if (!ep) return;
253
244
 
245
+ // Escaping using triple backslashes ensures client variables evaluate properly at runtime
254
246
  let html = \`
255
- <div class="endpoint-title">\${ep.title}</div>
247
+ <div class="endpoint-title">\\\${ep.title}</div>
256
248
  <div class="endpoint-path">
257
- <span class="method-badge \${ep.method}">\${ep.method}</span>
258
- <span>\${ep.path}</span>
249
+ <span class="method-badge \\\${ep.method}">\\\${ep.method}</span>
250
+ <span>\\\${ep.path}</span>
259
251
  </div>
260
- <div class="endpoint-desc">\${ep.desc}</div>
252
+ <div class="endpoint-desc">\\\${ep.desc}</div>
261
253
  \`;
262
254
 
263
255
  if (ep.params && ep.params.length) {
264
256
  html += \`<div class="form-section"><div class="form-section-title">Path Parameters</div>\`;
265
257
  ep.params.forEach(function(p) {
266
- const inputHtml = makeInputString('text', 'param-' + p.name, p.placeholder);
258
+ const ph = p.placeholder ? ' placeholder="' + p.placeholder + '"' : '';
267
259
  html += \`
268
260
  <div class="field-row">
269
- <label class="field-label">\${p.label}</label>
270
- \${inputHtml}
261
+ <label class="field-label">\\\${p.label}</label>
262
+ <input type="text" id="param-\\\${p.name}" \${ph} />
271
263
  </div>
272
264
  \`;
273
265
  });
@@ -277,11 +269,12 @@ function renderPanel(epKey) {
277
269
  if (ep.fields && ep.fields.length) {
278
270
  html += \`<div class="form-section"><div class="form-section-title">HTTP JSON Request Payload Parameters</div>\`;
279
271
  ep.fields.forEach(function(f) {
280
- const inputHtml = makeInputString(f.type || 'text', 'field-' + f.name, f.placeholder || '');
272
+ const type = f.type || 'text';
273
+ const ph = f.placeholder ? ' placeholder="' + f.placeholder + '"' : '';
281
274
  html += \`
282
275
  <div class="field-row">
283
- <label class="field-label">\${f.label}</label>
284
- \${inputHtml}
276
+ <label class="field-label">\\\${f.label}</label>
277
+ <input type="\${type}" id="field-\\\${f.name}" \${ph} />
285
278
  </div>
286
279
  \`;
287
280
  });
@@ -364,7 +357,6 @@ function setResponse(data, state, status, ms) {
364
357
  badge.textContent = status + ' · ' + ms + 'ms';
365
358
  body.className = 'response-body';
366
359
 
367
- // FIXED: Nested rendering inside dedicated horizontal scrolling code tag block wrapper elements
368
360
  const outputString = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
369
361
  body.innerHTML = '<pre class="json-render-block">' + highlightJson(outputString) + '</pre>';
370
362
  }
@@ -378,10 +370,11 @@ function clearResponse() {
378
370
  body.textContent = 'Execute a request row to generate feedback data';
379
371
  }
380
372
 
373
+ // Full syntax highlighting for JSON blocks
381
374
  function highlightJson(str) {
382
375
  return str
383
376
  .replace(/&/g, '&amp;').replace(/[<]/g, '&lt;').replace(/[>]/g, '&gt;')
384
- .replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function(match) {
377
+ .replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, function(match) {
385
378
  if (/^"/.test(match)) {
386
379
  if (/:$/.test(match)) return '<span class="json-key">' + match + '</span>';
387
380
  return '<span class="json-str">' + match + '</span>';
package/index.js CHANGED
@@ -54,23 +54,49 @@ export function endtesterExpress() {
54
54
  }
55
55
 
56
56
  // =========================
57
- // Extract req.body fields (FIXED MULTI-LINE)
57
+ // Context-Aware Static Fallbacks
58
+ // =========================
59
+ function getFallbackFieldsForPath(path) {
60
+ const lowerPath = path.toLowerCase();
61
+ let rawFields = [];
62
+
63
+ if (lowerPath.includes('login') || lowerPath.includes('auth') || lowerPath.includes('signin')) {
64
+ rawFields = ['email', 'password'];
65
+ } else if (lowerPath.includes('user') || lowerPath.includes('register') || lowerPath.includes('signup')) {
66
+ rawFields = ['username', 'email', 'password'];
67
+ } else if (lowerPath.includes('product')) {
68
+ rawFields = ['name', 'price', 'stock'];
69
+ } else {
70
+ return [];
71
+ }
72
+
73
+ return rawFields.map(field => ({
74
+ name: field,
75
+ label: field.charAt(0).toUpperCase() + field.slice(1),
76
+ type: detectInputType(field),
77
+ placeholder: `Enter ${field}`
78
+ }));
79
+ }
80
+
81
+ // =========================
82
+ // Extract req.body fields via Source Inspection
58
83
  // =========================
59
84
  function extractBodyFields(handler) {
60
85
  try {
61
86
  const source = handler.toString();
87
+
88
+ // If it's a bound handler or lacks source reference code text
89
+ if (!source || source.includes('[native code]')) return [];
62
90
 
63
- // FIXED: The 's' flag allows matching across multiple lines (\n)
64
91
  const regex = /(const|let|var)\s*\{\s*([^}]+)\s*\}\s*=\s*req\.body/gs;
65
92
  const matches = [...source.matchAll(regex)];
66
93
  const fields = [];
67
94
 
68
95
  matches.forEach((match) => {
69
- // Clean out formatting line-breaks, tabs, or rogue inner code comments
70
96
  const cleanedVariablesBlock = match[2]
71
- .replace(/\/\/.*$/gm, '') // strip inline comments
72
- .replace(/\/\*[\s\S]*?\*\//g, '') // strip block comments
73
- .replace(/[\r\n\t]/g, ' '); // normalize lines to spaces
97
+ .replace(/\/\/.*$/gm, '')
98
+ .replace(/\/\*[\s\S]*?\*\//g, '')
99
+ .replace(/[\r\n\t]/g, ' ');
74
100
 
75
101
  const variables = cleanedVariablesBlock
76
102
  .split(',')
@@ -80,29 +106,22 @@ export function endtesterExpress() {
80
106
  variables.forEach((field) => {
81
107
  let realField = field;
82
108
 
83
- // Support aliases (e.g., name: username)
84
109
  if (field.includes(':')) {
85
110
  realField = field.split(':')[0].trim();
86
111
  }
87
112
 
88
- // Remove defaults values (e.g., age = 0)
89
113
  if (realField.includes('=')) {
90
114
  realField = realField.split('=')[0].trim();
91
115
  }
92
116
 
93
- // Clean any trailing whitespace variations
94
117
  realField = realField.trim();
95
118
 
96
- const alreadyExists = fields.find(
97
- f => f.name === realField
98
- );
119
+ const alreadyExists = fields.find(f => f.name === realField);
99
120
 
100
121
  if (!alreadyExists && realField) {
101
122
  fields.push({
102
123
  name: realField,
103
- label:
104
- realField.charAt(0).toUpperCase() +
105
- realField.slice(1),
124
+ label: realField.charAt(0).toUpperCase() + realField.slice(1),
106
125
  type: detectInputType(realField),
107
126
  placeholder: `Enter ${realField}`
108
127
  });
@@ -128,13 +147,8 @@ export function endtesterExpress() {
128
147
  // ROUTES
129
148
  // =========================
130
149
  if (layer.route) {
131
- const methods = Object.keys(
132
- layer.route.methods
133
- );
134
-
135
- const path = (
136
- prefix + layer.route.path
137
- ).replace(/\/+/g, '/');
150
+ const methods = Object.keys(layer.route.methods);
151
+ const path = (prefix + layer.route.path).replace(/\/+/g, '/');
138
152
 
139
153
  if (path.includes('/api/tester')) {
140
154
  return;
@@ -142,14 +156,8 @@ export function endtesterExpress() {
142
156
 
143
157
  methods.forEach((method) => {
144
158
  const httpMethod = method.toUpperCase();
159
+ const key = `${httpMethod.toLowerCase()}-` + path.replace(/[^a-zA-Z0-9]/g, '-');
145
160
 
146
- const key =
147
- `${httpMethod.toLowerCase()}-` +
148
- path.replace(/[^a-zA-Z0-9]/g, '-');
149
-
150
- // =========================
151
- // PATH PARAMS
152
- // =========================
153
161
  const pathParams = layer.route.keys
154
162
  ? layer.route.keys.map((k) => ({
155
163
  name: k.name,
@@ -159,37 +167,27 @@ export function endtesterExpress() {
159
167
  : [];
160
168
 
161
169
  // =========================
162
- // BODY FIELDS
170
+ // BODY FIELDS COMPILING
163
171
  // =========================
164
172
  let bodyFields = [];
165
173
 
166
- if (
167
- ['POST', 'PUT', 'PATCH'].includes(
168
- httpMethod
169
- )
170
- ) {
174
+ if (['POST', 'PUT', 'PATCH'].includes(httpMethod)) {
171
175
  layer.route.stack.forEach((stackLayer) => {
172
- if (
173
- stackLayer.handle &&
174
- typeof stackLayer.handle === 'function'
175
- ) {
176
- const extractedFields =
177
- extractBodyFields(
178
- stackLayer.handle
179
- );
180
-
176
+ if (stackLayer.handle && typeof stackLayer.handle === 'function') {
177
+ const extractedFields = extractBodyFields(stackLayer.handle);
181
178
  bodyFields.push(...extractedFields);
182
179
  }
183
180
  });
184
181
 
185
- // Remove duplicates
182
+ // Deduplicate discovered elements
186
183
  bodyFields = bodyFields.filter(
187
- (field, index, self) =>
188
- index ===
189
- self.findIndex(
190
- f => f.name === field.name
191
- )
184
+ (field, index, self) => index === self.findIndex(f => f.name === field.name)
192
185
  );
186
+
187
+ // CRITICAL FAILSAFE: If code reflection extracted nothing, apply smart path-based fallback fields
188
+ if (bodyFields.length === 0) {
189
+ bodyFields = getFallbackFieldsForPath(path);
190
+ }
193
191
  }
194
192
 
195
193
  detectedEndpoints[key] = {
@@ -219,10 +217,7 @@ export function endtesterExpress() {
219
217
  .match(/^\/\^\\(.*?)\\\/\?/);
220
218
 
221
219
  if (match && match[1]) {
222
- routerPath = match[1].replace(
223
- /\\/g,
224
- ''
225
- );
220
+ routerPath = match[1].replace(/\\/g, '');
226
221
  }
227
222
  }
228
223
 
@@ -237,24 +232,15 @@ export function endtesterExpress() {
237
232
  // =========================
238
233
  // START PARSING
239
234
  // =========================
240
- if (
241
- expressApp._router &&
242
- expressApp._router.stack
243
- ) {
235
+ if (expressApp._router && expressApp._router.stack) {
244
236
  parseStack(expressApp._router.stack);
245
237
  }
246
238
 
247
239
  // =========================
248
240
  // RENDER HTML
249
241
  // =========================
250
- const fullHtml =
251
- getHtmlTemplate(detectedEndpoints);
252
-
253
- res.setHeader(
254
- 'Content-Type',
255
- 'text/html'
256
- );
257
-
242
+ const fullHtml = getHtmlTemplate(detectedEndpoints);
243
+ res.setHeader('Content-Type', 'text/html');
258
244
  return res.send(fullHtml);
259
245
  };
260
246
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aimeloic/monkey-tester",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "description": "Auto route scanning visual runner dashboard.",
5
5
  "main": "index.js",
6
6
  "type": "module",