@aimeloic/monkey-tester 2.0.1 → 2.0.3
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 +270 -1038
- package/index.js +59 -73
- package/package.json +1 -1
package/htmlTemplate.js
CHANGED
|
@@ -1,1172 +1,404 @@
|
|
|
1
1
|
export function getHtmlTemplate(endpoints) {
|
|
2
|
-
const safeJsonString = Buffer
|
|
3
|
-
.from(JSON.stringify(endpoints))
|
|
4
|
-
.toString('base64');
|
|
2
|
+
const safeJsonString = Buffer.from(JSON.stringify(endpoints)).toString('base64');
|
|
5
3
|
|
|
6
4
|
return `
|
|
7
5
|
<!DOCTYPE html>
|
|
8
6
|
<html lang="en">
|
|
9
7
|
<head>
|
|
10
|
-
<meta charset="UTF-8"
|
|
11
|
-
<meta
|
|
12
|
-
name="viewport"
|
|
13
|
-
content="width=device-width, initial-scale=1.0"
|
|
14
|
-
/>
|
|
15
|
-
|
|
8
|
+
<meta charset="UTF-8">
|
|
9
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
16
10
|
<title>Endtester — API Environment</title>
|
|
17
|
-
|
|
18
|
-
<link
|
|
19
|
-
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"
|
|
20
|
-
rel="stylesheet"
|
|
21
|
-
/>
|
|
22
|
-
|
|
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">
|
|
23
12
|
<style>
|
|
24
|
-
:root{
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
*{
|
|
39
|
-
box-sizing:border-box;
|
|
40
|
-
margin:0;
|
|
41
|
-
padding:0;
|
|
42
|
-
min-width:0;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
html,
|
|
46
|
-
body{
|
|
47
|
-
width:100%;
|
|
48
|
-
overflow-x:hidden;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
body{
|
|
52
|
-
background:var(--bg);
|
|
53
|
-
color:var(--text);
|
|
54
|
-
font-family:'DM Sans',sans-serif;
|
|
55
|
-
font-size:14px;
|
|
56
|
-
min-height:100vh;
|
|
57
|
-
overflow-x:hidden;
|
|
58
|
-
|
|
59
|
-
background-image:
|
|
60
|
-
radial-gradient(
|
|
61
|
-
ellipse 80% 60% at 50% -20%,
|
|
62
|
-
#3a2a0a22 0%,
|
|
63
|
-
transparent 70%
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
header{
|
|
68
|
-
width:100%;
|
|
69
|
-
border-bottom:1px solid var(--border);
|
|
70
|
-
|
|
71
|
-
padding:20px 24px;
|
|
72
|
-
|
|
73
|
-
display:flex;
|
|
74
|
-
align-items:center;
|
|
75
|
-
gap:20px;
|
|
76
|
-
|
|
77
|
-
background:#0e0c09ee;
|
|
78
|
-
|
|
79
|
-
backdrop-filter:blur(8px);
|
|
80
|
-
|
|
81
|
-
position:sticky;
|
|
82
|
-
top:0;
|
|
83
|
-
z-index:100;
|
|
84
|
-
|
|
85
|
-
overflow:hidden;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.logo{
|
|
89
|
-
font-family:'Playfair Display',serif;
|
|
90
|
-
font-size:22px;
|
|
91
|
-
color:var(--accent);
|
|
92
|
-
letter-spacing:.02em;
|
|
93
|
-
flex-shrink:0;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.logo span{
|
|
97
|
-
color:var(--text-dim);
|
|
98
|
-
font-size:13px;
|
|
99
|
-
font-family:'DM Mono',monospace;
|
|
100
|
-
display:block;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
.header-right{
|
|
104
|
-
margin-left:auto;
|
|
105
|
-
|
|
106
|
-
display:flex;
|
|
107
|
-
align-items:center;
|
|
108
|
-
gap:12px;
|
|
109
|
-
|
|
110
|
-
flex-wrap:wrap;
|
|
111
|
-
justify-content:flex-end;
|
|
112
|
-
|
|
113
|
-
max-width:100%;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
.jwt-wrap,
|
|
117
|
-
.base-url-wrap{
|
|
118
|
-
display:flex;
|
|
119
|
-
align-items:center;
|
|
120
|
-
gap:8px;
|
|
121
|
-
|
|
122
|
-
min-width:0;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.jwt-wrap label,
|
|
126
|
-
.base-url-wrap label{
|
|
127
|
-
color:var(--text-dim);
|
|
128
|
-
font-size:12px;
|
|
129
|
-
font-family:'DM Mono',monospace;
|
|
130
|
-
white-space:nowrap;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
#jwt-input,
|
|
134
|
-
#base-url{
|
|
135
|
-
background:var(--surface2);
|
|
136
|
-
|
|
137
|
-
border:1px solid var(--border);
|
|
138
|
-
|
|
139
|
-
color:var(--text);
|
|
140
|
-
|
|
141
|
-
font-family:'DM Mono',monospace;
|
|
142
|
-
|
|
143
|
-
font-size:11px;
|
|
144
|
-
|
|
145
|
-
padding:6px 10px;
|
|
146
|
-
|
|
147
|
-
border-radius:var(--radius);
|
|
148
|
-
|
|
149
|
-
width:220px;
|
|
150
|
-
max-width:100%;
|
|
151
|
-
|
|
152
|
-
outline:none;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
.layout{
|
|
156
|
-
width:100%;
|
|
157
|
-
max-width:100vw;
|
|
158
|
-
|
|
159
|
-
display:grid;
|
|
160
|
-
|
|
161
|
-
grid-template-columns:
|
|
162
|
-
minmax(220px,260px)
|
|
163
|
-
minmax(0,1fr)
|
|
164
|
-
minmax(300px,400px);
|
|
165
|
-
|
|
166
|
-
height:calc(100vh - 78px);
|
|
167
|
-
|
|
168
|
-
overflow:hidden;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
aside{
|
|
172
|
-
border-right:1px solid var(--border);
|
|
173
|
-
|
|
174
|
-
overflow-y:auto;
|
|
175
|
-
overflow-x:hidden;
|
|
176
|
-
|
|
177
|
-
padding:16px 0;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
.section-label{
|
|
181
|
-
font-size:10px;
|
|
182
|
-
|
|
183
|
-
font-family:'DM Mono',monospace;
|
|
184
|
-
|
|
185
|
-
color:var(--text-dim);
|
|
186
|
-
|
|
187
|
-
letter-spacing:.12em;
|
|
188
|
-
|
|
189
|
-
text-transform:uppercase;
|
|
190
|
-
|
|
191
|
-
padding:12px 18px 6px;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
.nav-item{
|
|
195
|
-
display:flex;
|
|
196
|
-
align-items:center;
|
|
197
|
-
gap:10px;
|
|
198
|
-
|
|
199
|
-
padding:8px 18px;
|
|
200
|
-
|
|
201
|
-
cursor:pointer;
|
|
202
|
-
|
|
203
|
-
border-left:2px solid transparent;
|
|
204
|
-
|
|
205
|
-
color:var(--text-dim);
|
|
206
|
-
|
|
207
|
-
overflow:hidden;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
.nav-item:hover{
|
|
211
|
-
background:var(--surface);
|
|
212
|
-
color:var(--text);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
.nav-item.active{
|
|
216
|
-
border-left-color:var(--accent);
|
|
217
|
-
|
|
218
|
-
background:var(--surface);
|
|
219
|
-
|
|
220
|
-
color:var(--accent);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
.method-badge{
|
|
224
|
-
font-family:'DM Mono',monospace;
|
|
225
|
-
|
|
226
|
-
font-size:9px;
|
|
227
|
-
|
|
228
|
-
font-weight:500;
|
|
229
|
-
|
|
230
|
-
padding:2px 5px;
|
|
231
|
-
|
|
232
|
-
border-radius:3px;
|
|
233
|
-
|
|
234
|
-
min-width:45px;
|
|
235
|
-
|
|
236
|
-
text-align:center;
|
|
237
|
-
|
|
238
|
-
flex-shrink:0;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
.GET{
|
|
242
|
-
background:#1a3a22;
|
|
243
|
-
color:#6ba05a;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
.POST{
|
|
247
|
-
background:#1a2e3a;
|
|
248
|
-
color:#5a86c0;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
.PUT,
|
|
252
|
-
.PATCH{
|
|
253
|
-
background:#3a2e10;
|
|
254
|
-
color:#e8a838;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
.DELETE{
|
|
258
|
-
background:#3a1a14;
|
|
259
|
-
color:#d45c3c;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
.nav-label{
|
|
263
|
-
font-size:11px;
|
|
264
|
-
|
|
265
|
-
flex:1;
|
|
266
|
-
|
|
267
|
-
overflow:hidden;
|
|
268
|
-
|
|
269
|
-
text-overflow:ellipsis;
|
|
270
|
-
|
|
271
|
-
white-space:nowrap;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
main{
|
|
275
|
-
overflow-y:auto;
|
|
276
|
-
overflow-x:hidden;
|
|
277
|
-
|
|
278
|
-
padding:24px;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
.endpoint-title{
|
|
282
|
-
font-family:'Playfair Display',serif;
|
|
283
|
-
|
|
284
|
-
font-size:20px;
|
|
285
|
-
|
|
286
|
-
color:var(--accent);
|
|
287
|
-
|
|
288
|
-
margin-bottom:6px;
|
|
289
|
-
|
|
290
|
-
overflow-wrap:anywhere;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
.endpoint-path{
|
|
294
|
-
font-family:'DM Mono',monospace;
|
|
295
|
-
|
|
296
|
-
font-size:12px;
|
|
297
|
-
|
|
298
|
-
color:var(--text-dim);
|
|
299
|
-
|
|
300
|
-
margin-bottom:20px;
|
|
301
|
-
|
|
302
|
-
display:flex;
|
|
303
|
-
align-items:center;
|
|
304
|
-
gap:8px;
|
|
305
|
-
|
|
306
|
-
flex-wrap:wrap;
|
|
307
|
-
|
|
308
|
-
overflow-wrap:anywhere;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
.endpoint-desc{
|
|
312
|
-
color:var(--text-dim);
|
|
313
|
-
|
|
314
|
-
font-size:13px;
|
|
315
|
-
|
|
316
|
-
line-height:1.6;
|
|
317
|
-
|
|
318
|
-
margin-bottom:24px;
|
|
319
|
-
|
|
320
|
-
border-left:2px solid var(--border);
|
|
321
|
-
|
|
322
|
-
padding-left:12px;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
.form-section{
|
|
326
|
-
background:var(--surface);
|
|
327
|
-
|
|
328
|
-
border:1px solid var(--border);
|
|
329
|
-
|
|
330
|
-
border-radius:var(--radius);
|
|
331
|
-
|
|
332
|
-
padding:18px;
|
|
333
|
-
|
|
334
|
-
margin-bottom:16px;
|
|
335
|
-
|
|
336
|
-
overflow:hidden;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
.form-section-title{
|
|
340
|
-
font-size:11px;
|
|
341
|
-
|
|
342
|
-
font-family:'DM Mono',monospace;
|
|
343
|
-
|
|
344
|
-
color:var(--text-dim);
|
|
345
|
-
|
|
346
|
-
letter-spacing:.1em;
|
|
347
|
-
|
|
348
|
-
text-transform:uppercase;
|
|
349
|
-
|
|
350
|
-
margin-bottom:14px;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
.field-row{
|
|
354
|
-
display:grid;
|
|
355
|
-
|
|
356
|
-
grid-template-columns:
|
|
357
|
-
minmax(90px,140px)
|
|
358
|
-
minmax(0,1fr);
|
|
359
|
-
|
|
360
|
-
align-items:center;
|
|
361
|
-
|
|
362
|
-
gap:10px;
|
|
363
|
-
|
|
364
|
-
margin-bottom:10px;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
.field-label{
|
|
368
|
-
font-family:'DM Mono',monospace;
|
|
369
|
-
|
|
370
|
-
font-size:11px;
|
|
371
|
-
|
|
372
|
-
color:var(--text-dim);
|
|
373
|
-
|
|
374
|
-
text-align:right;
|
|
375
|
-
|
|
376
|
-
overflow-wrap:anywhere;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
input,
|
|
380
|
-
select,
|
|
381
|
-
textarea{
|
|
382
|
-
max-width:100%;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
input[type=text],
|
|
386
|
-
input[type=number],
|
|
387
|
-
input[type=password],
|
|
388
|
-
input[type=email],
|
|
389
|
-
input[type=date],
|
|
390
|
-
input[type=tel],
|
|
391
|
-
input[type=url],
|
|
392
|
-
select{
|
|
393
|
-
background:var(--surface2);
|
|
394
|
-
|
|
395
|
-
border:1px solid var(--border);
|
|
396
|
-
|
|
397
|
-
color:var(--text);
|
|
398
|
-
|
|
399
|
-
font-family:'DM Mono',monospace;
|
|
400
|
-
|
|
401
|
-
font-size:12px;
|
|
402
|
-
|
|
403
|
-
padding:7px 10px;
|
|
404
|
-
|
|
405
|
-
border-radius:var(--radius);
|
|
406
|
-
|
|
407
|
-
width:100%;
|
|
408
|
-
|
|
409
|
-
outline:none;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
.btn-row{
|
|
413
|
-
margin-top:20px;
|
|
414
|
-
|
|
415
|
-
display:flex;
|
|
416
|
-
gap:10px;
|
|
417
|
-
|
|
418
|
-
flex-wrap:wrap;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
.btn{
|
|
422
|
-
background:var(--accent);
|
|
423
|
-
|
|
424
|
-
color:#0e0c09;
|
|
425
|
-
|
|
426
|
-
border:none;
|
|
427
|
-
|
|
428
|
-
padding:10px 22px;
|
|
429
|
-
|
|
430
|
-
border-radius:var(--radius);
|
|
431
|
-
|
|
432
|
-
font-size:13px;
|
|
433
|
-
|
|
434
|
-
font-weight:500;
|
|
435
|
-
|
|
436
|
-
cursor:pointer;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
.btn-secondary{
|
|
440
|
-
background:var(--surface2);
|
|
441
|
-
|
|
442
|
-
color:var(--text-dim);
|
|
443
|
-
|
|
444
|
-
border:1px solid var(--border);
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
.response-panel{
|
|
448
|
-
border-left:1px solid var(--border);
|
|
449
|
-
|
|
450
|
-
display:flex;
|
|
451
|
-
|
|
452
|
-
flex-direction:column;
|
|
453
|
-
|
|
454
|
-
overflow:hidden;
|
|
455
|
-
|
|
456
|
-
min-width:0;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
.response-header{
|
|
460
|
-
padding:14px 18px;
|
|
461
|
-
|
|
462
|
-
border-bottom:1px solid var(--border);
|
|
463
|
-
|
|
464
|
-
display:flex;
|
|
465
|
-
align-items:center;
|
|
466
|
-
|
|
467
|
-
gap:10px;
|
|
468
|
-
|
|
469
|
-
background:var(--surface);
|
|
470
|
-
|
|
471
|
-
overflow:hidden;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
.response-header-title{
|
|
475
|
-
font-size:11px;
|
|
476
|
-
|
|
477
|
-
font-family:'DM Mono',monospace;
|
|
478
|
-
|
|
479
|
-
color:var(--text-dim);
|
|
480
|
-
|
|
481
|
-
text-transform:uppercase;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
.status-badge{
|
|
485
|
-
font-family:'DM Mono',monospace;
|
|
486
|
-
|
|
487
|
-
font-size:12px;
|
|
488
|
-
|
|
489
|
-
margin-left:auto;
|
|
490
|
-
|
|
491
|
-
padding:2px 8px;
|
|
492
|
-
|
|
493
|
-
border-radius:4px;
|
|
494
|
-
|
|
495
|
-
flex-shrink:0;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
.status-ok{
|
|
499
|
-
background:#1a3a22;
|
|
500
|
-
color:#6ba05a;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
.status-err{
|
|
504
|
-
background:#3a1a14;
|
|
505
|
-
color:#d45c3c;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
.status-idle{
|
|
509
|
-
background:var(--surface2);
|
|
510
|
-
color:var(--text-dim);
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
.response-body{
|
|
514
|
-
flex:1;
|
|
515
|
-
|
|
516
|
-
overflow:auto;
|
|
517
|
-
|
|
518
|
-
padding:16px;
|
|
519
|
-
|
|
520
|
-
font-family:'DM Mono',monospace;
|
|
521
|
-
|
|
522
|
-
font-size:12px;
|
|
523
|
-
|
|
524
|
-
white-space:pre-wrap;
|
|
525
|
-
|
|
526
|
-
overflow-wrap:anywhere;
|
|
527
|
-
|
|
528
|
-
word-break:break-word;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
.response-body.empty{
|
|
532
|
-
color:var(--text-dim);
|
|
533
|
-
|
|
534
|
-
display:flex;
|
|
535
|
-
|
|
536
|
-
align-items:center;
|
|
537
|
-
|
|
538
|
-
justify-content:center;
|
|
539
|
-
|
|
540
|
-
text-align:center;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
.json-key{color:#e8a838;}
|
|
544
|
-
.json-str{color:#9ab878;}
|
|
545
|
-
.json-num{color:#5a86c0;}
|
|
546
|
-
|
|
547
|
-
#toast{
|
|
548
|
-
position:fixed;
|
|
549
|
-
|
|
550
|
-
bottom:24px;
|
|
551
|
-
right:24px;
|
|
552
|
-
|
|
553
|
-
background:var(--surface2);
|
|
554
|
-
|
|
555
|
-
border:1px solid var(--border);
|
|
556
|
-
|
|
557
|
-
padding:10px 18px;
|
|
558
|
-
|
|
559
|
-
border-radius:var(--radius);
|
|
560
|
-
|
|
561
|
-
opacity:0;
|
|
562
|
-
|
|
563
|
-
transition:all .25s;
|
|
564
|
-
|
|
565
|
-
max-width:90vw;
|
|
566
|
-
|
|
567
|
-
overflow-wrap:anywhere;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
#toast.show{
|
|
571
|
-
opacity:1;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
@media (max-width:1200px){
|
|
575
|
-
|
|
576
|
-
.layout{
|
|
577
|
-
grid-template-columns:
|
|
578
|
-
220px
|
|
579
|
-
1fr;
|
|
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;
|
|
580
26
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
27
|
+
|
|
28
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
29
|
+
|
|
30
|
+
body {
|
|
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%);
|
|
584
38
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
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;
|
|
592
49
|
}
|
|
593
|
-
|
|
594
|
-
.
|
|
595
|
-
|
|
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; }
|
|
54
|
+
.jwt-wrap, .base-url-wrap { display: flex; align-items: center; gap: 8px; }
|
|
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
|
+
|
|
57
|
+
#jwt-input, #base-url {
|
|
58
|
+
background: var(--surface2); border: 1px solid var(--border); color: var(--text);
|
|
59
|
+
font-family: 'DM Mono', monospace; font-size: 12px; padding: 6px 12px; border-radius: var(--radius); width: 220px; outline: none;
|
|
596
60
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
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;
|
|
600
68
|
}
|
|
601
|
-
|
|
602
|
-
aside{
|
|
603
|
-
|
|
69
|
+
|
|
70
|
+
aside {
|
|
71
|
+
border-right: 1px solid var(--border);
|
|
72
|
+
overflow-y: auto;
|
|
73
|
+
padding: 16px 0;
|
|
74
|
+
background: #0b0907;
|
|
604
75
|
}
|
|
605
|
-
|
|
606
|
-
.
|
|
607
|
-
|
|
76
|
+
|
|
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; }
|
|
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; }
|
|
80
|
+
.nav-item:hover { background: var(--surface); color: var(--text); }
|
|
81
|
+
.nav-item.active { border-left-color: var(--accent); background: var(--surface); color: var(--accent); }
|
|
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; }
|
|
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
|
+
|
|
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;
|
|
608
95
|
}
|
|
609
|
-
|
|
610
|
-
.
|
|
611
|
-
|
|
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; }
|
|
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; }
|
|
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;
|
|
612
110
|
}
|
|
613
|
-
}
|
|
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; }
|
|
116
|
+
.btn-secondary { background: var(--surface2); color: var(--text-dim); border: 1px solid var(--border); }
|
|
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; }
|
|
130
|
+
.status-ok { background: #1a3a22; color: #6ba05a; }
|
|
131
|
+
.status-err { background: #3a1a14; color: #d45c3c; }
|
|
132
|
+
.status-idle { background: var(--surface2); color: var(--text-dim); }
|
|
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); }
|
|
171
|
+
#toast.show { opacity: 1; }
|
|
614
172
|
</style>
|
|
615
173
|
</head>
|
|
616
|
-
|
|
617
174
|
<body>
|
|
618
175
|
|
|
619
|
-
<div
|
|
620
|
-
id="endtester-data-vault"
|
|
621
|
-
data-payload="${safeJsonString}"
|
|
622
|
-
style="display:none;"
|
|
623
|
-
></div>
|
|
176
|
+
<div id="endtester-data-vault" data-payload="${safeJsonString}" style="display: none;"></div>
|
|
624
177
|
|
|
625
178
|
<header>
|
|
626
|
-
<div class="logo">
|
|
627
|
-
Endtester
|
|
628
|
-
<span>Application Runtime Sandbox</span>
|
|
629
|
-
</div>
|
|
630
|
-
|
|
179
|
+
<div class="logo">Endtester <span>Application Runtime Sandbox</span></div>
|
|
631
180
|
<div class="header-right">
|
|
632
|
-
|
|
633
181
|
<div class="base-url-wrap">
|
|
634
182
|
<label>TARGET HOST</label>
|
|
635
|
-
|
|
636
|
-
<input
|
|
637
|
-
id="base-url"
|
|
638
|
-
type="text"
|
|
639
|
-
value=""
|
|
640
|
-
/>
|
|
183
|
+
<input id="base-url" type="text" value="">
|
|
641
184
|
</div>
|
|
642
|
-
|
|
643
185
|
<div class="jwt-wrap">
|
|
644
186
|
<label>BEARER AUTH</label>
|
|
645
|
-
|
|
646
|
-
<input
|
|
647
|
-
id="jwt-input"
|
|
648
|
-
type="text"
|
|
649
|
-
placeholder="Token value..."
|
|
650
|
-
/>
|
|
187
|
+
<input id="jwt-input" type="text" placeholder="Token value...">
|
|
651
188
|
</div>
|
|
652
|
-
|
|
653
189
|
</div>
|
|
654
190
|
</header>
|
|
655
191
|
|
|
656
192
|
<div class="layout">
|
|
657
|
-
|
|
658
193
|
<aside id="sidebar-nav">
|
|
659
|
-
<div class="section-label">
|
|
660
|
-
Discovered Endpoints
|
|
661
|
-
</div>
|
|
194
|
+
<div class="section-label">Discovered Endpoints</div>
|
|
662
195
|
</aside>
|
|
663
|
-
|
|
664
196
|
<main id="main-panel"></main>
|
|
665
|
-
|
|
666
197
|
<div class="response-panel">
|
|
667
|
-
|
|
668
198
|
<div class="response-header">
|
|
669
|
-
<span class="response-header-title">
|
|
670
|
-
|
|
671
|
-
</span>
|
|
672
|
-
|
|
673
|
-
<span
|
|
674
|
-
id="status-badge"
|
|
675
|
-
class="status-badge status-idle"
|
|
676
|
-
>
|
|
677
|
-
—
|
|
678
|
-
</span>
|
|
679
|
-
</div>
|
|
680
|
-
|
|
681
|
-
<div
|
|
682
|
-
class="response-body empty"
|
|
683
|
-
id="response-body"
|
|
684
|
-
>
|
|
685
|
-
Execute a request row to generate feedback data
|
|
199
|
+
<span class="response-header-title">Response Output</span>
|
|
200
|
+
<span id="status-badge" class="status-badge status-idle">—</span>
|
|
686
201
|
</div>
|
|
687
|
-
|
|
202
|
+
<div class="response-body empty" id="response-body">Execute a request row to generate feedback data</div>
|
|
688
203
|
</div>
|
|
689
|
-
|
|
690
204
|
</div>
|
|
691
205
|
|
|
692
206
|
<div id="toast"></div>
|
|
693
207
|
|
|
694
208
|
<script>
|
|
695
|
-
const rawPayload =
|
|
696
|
-
|
|
697
|
-
.getElementById('endtester-data-vault')
|
|
698
|
-
.getAttribute('data-payload');
|
|
699
|
-
|
|
700
|
-
const ENDPOINTS =
|
|
701
|
-
JSON.parse(atob(rawPayload));
|
|
209
|
+
const rawPayload = document.getElementById('endtester-data-vault').getAttribute('data-payload');
|
|
210
|
+
const ENDPOINTS = JSON.parse(atob(rawPayload));
|
|
702
211
|
|
|
703
212
|
let currentEp = '';
|
|
704
213
|
|
|
705
|
-
document
|
|
706
|
-
.getElementById('base-url')
|
|
707
|
-
.value = window.location.origin;
|
|
708
|
-
|
|
709
|
-
function buildSidebar(){
|
|
710
|
-
|
|
711
|
-
const sidebar =
|
|
712
|
-
document.getElementById('sidebar-nav');
|
|
214
|
+
document.getElementById('base-url').value = window.location.origin;
|
|
713
215
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
if(keys.length === 0){
|
|
718
|
-
|
|
719
|
-
sidebar.innerHTML +=
|
|
720
|
-
'<div style="padding:15px;color:var(--text-dim)">No endpoints discovered.</div>';
|
|
216
|
+
function buildSidebar() {
|
|
217
|
+
const sidebar = document.getElementById('sidebar-nav');
|
|
218
|
+
const keys = Object.keys(ENDPOINTS);
|
|
721
219
|
|
|
220
|
+
if (keys.length === 0) {
|
|
221
|
+
sidebar.innerHTML += '<div style="padding:15px; color:var(--text-dim)">No active application endpoints discovered.</div>';
|
|
722
222
|
return;
|
|
723
223
|
}
|
|
724
224
|
|
|
725
|
-
keys.forEach((key,index)=>{
|
|
726
|
-
|
|
225
|
+
keys.forEach((key, index) => {
|
|
727
226
|
const ep = ENDPOINTS[key];
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
div.
|
|
733
|
-
|
|
734
|
-
? 'nav-item active'
|
|
735
|
-
: 'nav-item';
|
|
736
|
-
|
|
737
|
-
div.setAttribute('data-ep',key);
|
|
738
|
-
|
|
739
|
-
div.innerHTML =
|
|
740
|
-
'<span class="method-badge ' + ep.method + '">' +
|
|
741
|
-
ep.method +
|
|
742
|
-
'</span>' +
|
|
743
|
-
'<span class="nav-label">' +
|
|
744
|
-
ep.path +
|
|
745
|
-
'</span>';
|
|
746
|
-
|
|
747
|
-
div.addEventListener('click',()=>{
|
|
748
|
-
|
|
749
|
-
document
|
|
750
|
-
.querySelectorAll('.nav-item')
|
|
751
|
-
.forEach(n=>n.classList.remove('active'));
|
|
752
|
-
|
|
227
|
+
const div = document.createElement('div');
|
|
228
|
+
div.className = index === 0 ? 'nav-item active' : 'nav-item';
|
|
229
|
+
div.setAttribute('data-ep', key);
|
|
230
|
+
div.innerHTML = '<span class="method-badge ' + ep.method + '">' + ep.method + '</span><span class="nav-label">' + ep.path + '</span>';
|
|
231
|
+
div.addEventListener('click', () => {
|
|
232
|
+
document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
|
|
753
233
|
div.classList.add('active');
|
|
754
|
-
|
|
755
234
|
clearResponse();
|
|
756
|
-
|
|
757
235
|
renderPanel(key);
|
|
758
236
|
});
|
|
759
|
-
|
|
760
237
|
sidebar.appendChild(div);
|
|
761
238
|
});
|
|
762
239
|
|
|
763
|
-
if(keys.length > 0)
|
|
764
|
-
renderPanel(keys[0]);
|
|
765
|
-
}
|
|
240
|
+
if (keys.length > 0) renderPanel(keys[0]);
|
|
766
241
|
}
|
|
767
242
|
|
|
768
|
-
function makeInputString(
|
|
769
|
-
|
|
770
|
-
id
|
|
771
|
-
placeholder,
|
|
772
|
-
defaultValue
|
|
773
|
-
){
|
|
774
|
-
|
|
775
|
-
const pAttr =
|
|
776
|
-
placeholder
|
|
777
|
-
? ' placeholder="' + placeholder + '"'
|
|
778
|
-
: '';
|
|
779
|
-
|
|
780
|
-
const vAttr =
|
|
781
|
-
defaultValue !== undefined
|
|
782
|
-
? ' value="' + defaultValue + '"'
|
|
783
|
-
: '';
|
|
784
|
-
|
|
785
|
-
return \`
|
|
786
|
-
<input
|
|
787
|
-
type="\${type}"
|
|
788
|
-
id="\${id}"
|
|
789
|
-
\${pAttr}
|
|
790
|
-
\${vAttr}
|
|
791
|
-
/>
|
|
792
|
-
\`;
|
|
243
|
+
function makeInputString(type, id, placeholder) {
|
|
244
|
+
const pAttr = placeholder ? ' placeholder="' + placeholder + '"' : '';
|
|
245
|
+
return '<input type="' + type + '" id="' + id + '"' + pAttr + ' />';
|
|
793
246
|
}
|
|
794
247
|
|
|
795
|
-
function renderPanel(epKey){
|
|
796
|
-
|
|
248
|
+
function renderPanel(epKey) {
|
|
797
249
|
currentEp = epKey;
|
|
798
|
-
|
|
799
250
|
const ep = ENDPOINTS[epKey];
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
document.getElementById('main-panel');
|
|
803
|
-
|
|
804
|
-
if(!ep) return;
|
|
251
|
+
const main = document.getElementById('main-panel');
|
|
252
|
+
if (!ep) return;
|
|
805
253
|
|
|
806
254
|
let html = \`
|
|
807
|
-
<div class="endpoint-title">
|
|
808
|
-
\${ep.title}
|
|
809
|
-
</div>
|
|
810
|
-
|
|
255
|
+
<div class="endpoint-title">\${ep.title}</div>
|
|
811
256
|
<div class="endpoint-path">
|
|
812
|
-
<span class="method-badge \${ep.method}">
|
|
813
|
-
|
|
814
|
-
</span>
|
|
815
|
-
|
|
816
|
-
<span>
|
|
817
|
-
\${ep.path}
|
|
818
|
-
</span>
|
|
819
|
-
</div>
|
|
820
|
-
|
|
821
|
-
<div class="endpoint-desc">
|
|
822
|
-
\${ep.desc}
|
|
257
|
+
<span class="method-badge \${ep.method}">\${ep.method}</span>
|
|
258
|
+
<span>\${ep.path}</span>
|
|
823
259
|
</div>
|
|
260
|
+
<div class="endpoint-desc">\${ep.desc}</div>
|
|
824
261
|
\`;
|
|
825
262
|
|
|
826
|
-
if(ep.params && ep.params.length){
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
<div class="form-section-title">
|
|
832
|
-
Path Parameters
|
|
833
|
-
</div>
|
|
834
|
-
\`;
|
|
835
|
-
|
|
836
|
-
ep.params.forEach((p)=>{
|
|
837
|
-
|
|
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);
|
|
838
267
|
html += \`
|
|
839
268
|
<div class="field-row">
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
\${p.label}
|
|
843
|
-
</label>
|
|
844
|
-
|
|
845
|
-
\${makeInputString(
|
|
846
|
-
'text',
|
|
847
|
-
'param-' + p.name,
|
|
848
|
-
p.placeholder,
|
|
849
|
-
''
|
|
850
|
-
)}
|
|
851
|
-
|
|
269
|
+
<label class="field-label">\${p.label}</label>
|
|
270
|
+
\${inputHtml}
|
|
852
271
|
</div>
|
|
853
272
|
\`;
|
|
854
273
|
});
|
|
855
|
-
|
|
856
|
-
html += '</div>';
|
|
274
|
+
html += \`</div>\`;
|
|
857
275
|
}
|
|
858
276
|
|
|
859
|
-
if(ep.fields && ep.fields.length){
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
<div class="form-section-title">
|
|
865
|
-
Request Body
|
|
866
|
-
</div>
|
|
867
|
-
\`;
|
|
868
|
-
|
|
869
|
-
ep.fields.forEach((f)=>{
|
|
870
|
-
|
|
277
|
+
if (ep.fields && ep.fields.length) {
|
|
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 || '');
|
|
871
281
|
html += \`
|
|
872
282
|
<div class="field-row">
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
\${f.label}
|
|
876
|
-
</label>
|
|
877
|
-
|
|
878
|
-
\${makeInputString(
|
|
879
|
-
f.type || 'text',
|
|
880
|
-
'field-' + f.name,
|
|
881
|
-
f.placeholder || '',
|
|
882
|
-
''
|
|
883
|
-
)}
|
|
884
|
-
|
|
283
|
+
<label class="field-label">\${f.label}</label>
|
|
284
|
+
\${inputHtml}
|
|
885
285
|
</div>
|
|
886
286
|
\`;
|
|
887
287
|
});
|
|
888
|
-
|
|
889
|
-
html += '</div>';
|
|
288
|
+
html += \`</div>\`;
|
|
890
289
|
}
|
|
891
290
|
|
|
892
291
|
html += \`
|
|
893
292
|
<div class="btn-row">
|
|
894
|
-
|
|
895
|
-
<button
|
|
896
|
-
class="btn"
|
|
897
|
-
onclick="sendRequest()"
|
|
898
|
-
>
|
|
899
|
-
Execute Route
|
|
900
|
-
</button>
|
|
901
|
-
|
|
902
|
-
<button
|
|
903
|
-
class="btn btn-secondary"
|
|
904
|
-
onclick="clearResponse()"
|
|
905
|
-
>
|
|
906
|
-
Clear Context
|
|
907
|
-
</button>
|
|
908
|
-
|
|
293
|
+
<button class="btn" onclick="sendRequest()">Execute Route</button>
|
|
294
|
+
<button class="btn btn-secondary" onclick="clearResponse()">Clear Context</button>
|
|
909
295
|
</div>
|
|
910
296
|
\`;
|
|
911
297
|
|
|
912
298
|
main.innerHTML = html;
|
|
913
299
|
}
|
|
914
300
|
|
|
915
|
-
async function sendRequest(){
|
|
916
|
-
|
|
917
|
-
const ep =
|
|
918
|
-
ENDPOINTS[currentEp];
|
|
919
|
-
|
|
301
|
+
async function sendRequest() {
|
|
302
|
+
const ep = ENDPOINTS[currentEp];
|
|
920
303
|
let path = ep.path;
|
|
921
304
|
|
|
922
|
-
if(ep.params){
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
document
|
|
928
|
-
.getElementById('param-' + p.name)
|
|
929
|
-
?.value
|
|
930
|
-
.trim();
|
|
931
|
-
|
|
932
|
-
if(!val){
|
|
933
|
-
|
|
934
|
-
showToast(
|
|
935
|
-
'Parameter required: ' + p.label
|
|
936
|
-
);
|
|
937
|
-
|
|
938
|
-
return;
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
path =
|
|
942
|
-
path.replace(
|
|
943
|
-
':' + p.name,
|
|
944
|
-
encodeURIComponent(val)
|
|
945
|
-
);
|
|
305
|
+
if (ep.params) {
|
|
306
|
+
for (const p of ep.params) {
|
|
307
|
+
const val = document.getElementById('param-' + p.name)?.value.trim();
|
|
308
|
+
if (!val) { showToast('Warning: Parameter ' + p.label + ' is required'); return; }
|
|
309
|
+
path = path.replace(':' + p.name, encodeURIComponent(val));
|
|
946
310
|
}
|
|
947
311
|
}
|
|
948
312
|
|
|
949
|
-
const baseUrl =
|
|
950
|
-
document
|
|
951
|
-
.getElementById('base-url')
|
|
952
|
-
.value
|
|
953
|
-
.replace(/[/]+$/,'');
|
|
954
|
-
|
|
313
|
+
const baseUrl = document.getElementById('base-url').value.replace(/[/]+$/, '');
|
|
955
314
|
const url = baseUrl + path;
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
.
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
let body;
|
|
973
|
-
|
|
974
|
-
if(
|
|
975
|
-
ep.fields &&
|
|
976
|
-
ep.fields.length &&
|
|
977
|
-
['POST','PUT','PATCH']
|
|
978
|
-
.includes(ep.method)
|
|
979
|
-
){
|
|
980
|
-
|
|
981
|
-
const payloadObject = {};
|
|
982
|
-
|
|
983
|
-
ep.fields.forEach((f)=>{
|
|
984
|
-
|
|
985
|
-
const input =
|
|
986
|
-
document.getElementById(
|
|
987
|
-
'field-' + f.name
|
|
988
|
-
);
|
|
989
|
-
|
|
990
|
-
if(!input) return;
|
|
991
|
-
|
|
992
|
-
let value = input.value;
|
|
993
|
-
|
|
994
|
-
if(f.type === 'number'){
|
|
995
|
-
value =
|
|
996
|
-
value === ''
|
|
997
|
-
? null
|
|
998
|
-
: Number(value);
|
|
315
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
316
|
+
|
|
317
|
+
const jwt = document.getElementById('jwt-input').value.trim();
|
|
318
|
+
if (jwt) headers['Authorization'] = 'Bearer ' + jwt;
|
|
319
|
+
|
|
320
|
+
let body = undefined;
|
|
321
|
+
if (ep.fields && ep.fields.length && ['POST', 'PUT', 'PATCH'].includes(ep.method)) {
|
|
322
|
+
const jsonPayload = {};
|
|
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;
|
|
999
331
|
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
});
|
|
1003
|
-
|
|
1004
|
-
body = JSON.stringify(payloadObject);
|
|
332
|
+
}
|
|
333
|
+
body = JSON.stringify(jsonPayload);
|
|
1005
334
|
}
|
|
1006
335
|
|
|
1007
|
-
setResponse(null,'loading');
|
|
1008
|
-
|
|
336
|
+
setResponse(null, 'loading');
|
|
1009
337
|
const start = Date.now();
|
|
1010
338
|
|
|
1011
|
-
try{
|
|
1012
|
-
|
|
1013
|
-
const
|
|
1014
|
-
|
|
1015
|
-
method:ep.method,
|
|
1016
|
-
headers,
|
|
1017
|
-
body
|
|
1018
|
-
});
|
|
1019
|
-
|
|
1020
|
-
const ms =
|
|
1021
|
-
Date.now() - start;
|
|
1022
|
-
|
|
1023
|
-
const text =
|
|
1024
|
-
await res.text();
|
|
1025
|
-
|
|
339
|
+
try {
|
|
340
|
+
const res = await fetch(url, { method: ep.method, headers, body });
|
|
341
|
+
const ms = Date.now() - start;
|
|
342
|
+
const text = await res.text();
|
|
1026
343
|
let json;
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
}
|
|
1031
|
-
json = text;
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
setResponse(
|
|
1035
|
-
json,
|
|
1036
|
-
res.ok ? 'ok' : 'err',
|
|
1037
|
-
res.status,
|
|
1038
|
-
ms
|
|
1039
|
-
);
|
|
1040
|
-
|
|
1041
|
-
}catch(err){
|
|
1042
|
-
|
|
1043
|
-
setResponse(
|
|
1044
|
-
{ error: err.message },
|
|
1045
|
-
'err',
|
|
1046
|
-
'FAIL',
|
|
1047
|
-
0
|
|
1048
|
-
);
|
|
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);
|
|
1049
348
|
}
|
|
1050
349
|
}
|
|
1051
350
|
|
|
1052
|
-
function setResponse(
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
status,
|
|
1056
|
-
ms
|
|
1057
|
-
){
|
|
1058
|
-
|
|
1059
|
-
const badge =
|
|
1060
|
-
document.getElementById(
|
|
1061
|
-
'status-badge'
|
|
1062
|
-
);
|
|
1063
|
-
|
|
1064
|
-
const body =
|
|
1065
|
-
document.getElementById(
|
|
1066
|
-
'response-body'
|
|
1067
|
-
);
|
|
1068
|
-
|
|
1069
|
-
if(state === 'loading'){
|
|
1070
|
-
|
|
1071
|
-
badge.className =
|
|
1072
|
-
'status-badge status-idle';
|
|
351
|
+
function setResponse(data, state, status, ms) {
|
|
352
|
+
const badge = document.getElementById('status-badge');
|
|
353
|
+
const body = document.getElementById('response-body');
|
|
1073
354
|
|
|
355
|
+
if (state === 'loading') {
|
|
356
|
+
badge.className = 'status-badge status-idle';
|
|
1074
357
|
badge.textContent = '...';
|
|
1075
|
-
|
|
1076
|
-
body.innerHTML =
|
|
1077
|
-
'Executing transmission...';
|
|
1078
|
-
|
|
358
|
+
body.className = 'response-body empty';
|
|
359
|
+
body.innerHTML = 'Executing transmission...';
|
|
1079
360
|
return;
|
|
1080
361
|
}
|
|
1081
362
|
|
|
1082
|
-
badge.className =
|
|
1083
|
-
|
|
1084
|
-
(
|
|
1085
|
-
state === 'ok'
|
|
1086
|
-
? 'status-ok'
|
|
1087
|
-
: 'status-err'
|
|
1088
|
-
);
|
|
1089
|
-
|
|
1090
|
-
badge.textContent =
|
|
1091
|
-
status + ' · ' + ms + 'ms';
|
|
1092
|
-
|
|
363
|
+
badge.className = 'status-badge ' + (state === 'ok' ? 'status-ok' : 'status-err');
|
|
364
|
+
badge.textContent = status + ' · ' + ms + 'ms';
|
|
1093
365
|
body.className = 'response-body';
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
? data
|
|
1099
|
-
: JSON.stringify(data,null,2)
|
|
1100
|
-
);
|
|
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>';
|
|
1101
370
|
}
|
|
1102
371
|
|
|
1103
|
-
function clearResponse(){
|
|
1104
|
-
|
|
1105
|
-
const
|
|
1106
|
-
|
|
1107
|
-
'status-badge'
|
|
1108
|
-
);
|
|
1109
|
-
|
|
1110
|
-
const body =
|
|
1111
|
-
document.getElementById(
|
|
1112
|
-
'response-body'
|
|
1113
|
-
);
|
|
1114
|
-
|
|
1115
|
-
badge.className =
|
|
1116
|
-
'status-badge status-idle';
|
|
1117
|
-
|
|
372
|
+
function clearResponse() {
|
|
373
|
+
const badge = document.getElementById('status-badge');
|
|
374
|
+
const body = document.getElementById('response-body');
|
|
375
|
+
badge.className = 'status-badge status-idle';
|
|
1118
376
|
badge.textContent = '—';
|
|
1119
|
-
|
|
1120
|
-
body.
|
|
1121
|
-
'response-body empty';
|
|
1122
|
-
|
|
1123
|
-
body.textContent =
|
|
1124
|
-
'Execute a request row to generate feedback data';
|
|
377
|
+
body.className = 'response-body empty';
|
|
378
|
+
body.textContent = 'Execute a request row to generate feedback data';
|
|
1125
379
|
}
|
|
1126
380
|
|
|
1127
|
-
function highlightJson(str){
|
|
1128
|
-
|
|
381
|
+
function highlightJson(str) {
|
|
1129
382
|
return str
|
|
1130
|
-
.replace(/&/g,'&')
|
|
1131
|
-
.replace(/[
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
/("(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\"])*"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g,
|
|
1136
|
-
|
|
1137
|
-
function(match){
|
|
1138
|
-
|
|
1139
|
-
if(/^"/.test(match)){
|
|
1140
|
-
|
|
1141
|
-
if(/:$/.test(match)){
|
|
1142
|
-
return '<span class="json-key">' + match + '</span>';
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
return '<span class="json-str">' + match + '</span>';
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
return '<span class="json-num">' + match + '</span>';
|
|
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>';
|
|
1149
388
|
}
|
|
1150
|
-
|
|
389
|
+
return '<span class="json-num">' + match + '</span>';
|
|
390
|
+
});
|
|
1151
391
|
}
|
|
1152
392
|
|
|
1153
|
-
function showToast(msg){
|
|
1154
|
-
|
|
1155
|
-
const t =
|
|
1156
|
-
document.getElementById('toast');
|
|
1157
|
-
|
|
393
|
+
function showToast(msg) {
|
|
394
|
+
const t = document.getElementById('toast');
|
|
1158
395
|
t.textContent = msg;
|
|
1159
|
-
|
|
1160
396
|
t.classList.add('show');
|
|
1161
|
-
|
|
1162
|
-
setTimeout(()=>{
|
|
1163
|
-
t.classList.remove('show');
|
|
1164
|
-
},2500);
|
|
397
|
+
setTimeout(() => t.classList.remove('show'), 2500);
|
|
1165
398
|
}
|
|
1166
399
|
|
|
1167
400
|
buildSidebar();
|
|
1168
401
|
</script>
|
|
1169
|
-
|
|
1170
402
|
</body>
|
|
1171
403
|
</html>
|
|
1172
404
|
`;
|