@aimeloic/monkey-tester 2.0.0 → 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/htmlTemplate.js CHANGED
@@ -1,316 +1,1172 @@
1
1
  export function getHtmlTemplate(endpoints) {
2
- const safeJsonString = Buffer.from(JSON.stringify(endpoints)).toString('base64');
2
+ const safeJsonString = Buffer
3
+ .from(JSON.stringify(endpoints))
4
+ .toString('base64');
3
5
 
4
6
  return `
5
7
  <!DOCTYPE html>
6
8
  <html lang="en">
7
9
  <head>
8
- <meta charset="UTF-8">
9
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
10
+ <meta charset="UTF-8" />
11
+ <meta
12
+ name="viewport"
13
+ content="width=device-width, initial-scale=1.0"
14
+ />
15
+
10
16
  <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">
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
+
12
23
  <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;
24
+ :root{
25
+ --bg:#0e0c09;
26
+ --surface:#181510;
27
+ --surface2:#221d14;
28
+ --border:#3a3020;
29
+ --accent:#e8a838;
30
+ --text:#f0e8d8;
31
+ --text-dim:#9a8c78;
32
+ --red:#d45c3c;
33
+ --green:#6ba05a;
34
+ --blue:#5a86c0;
35
+ --radius:8px;
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;
580
+ }
581
+
582
+ .response-panel{
583
+ display:none;
584
+ }
585
+ }
586
+
587
+ @media (max-width:768px){
588
+
589
+ header{
590
+ flex-direction:column;
591
+ align-items:flex-start;
592
+ }
593
+
594
+ .header-right{
595
+ width:100%;
596
+ }
597
+
598
+ .layout{
599
+ grid-template-columns:1fr;
26
600
  }
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%);
601
+
602
+ aside{
603
+ display:none;
31
604
  }
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;
605
+
606
+ .field-row{
607
+ grid-template-columns:1fr;
35
608
  }
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;
609
+
610
+ .field-label{
611
+ text-align:left;
44
612
  }
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; }
613
+ }
81
614
  </style>
82
615
  </head>
616
+
83
617
  <body>
84
618
 
85
- <div id="endtester-data-vault" data-payload="${safeJsonString}" style="display: none;"></div>
619
+ <div
620
+ id="endtester-data-vault"
621
+ data-payload="${safeJsonString}"
622
+ style="display:none;"
623
+ ></div>
86
624
 
87
625
  <header>
88
- <div class="logo">Endtester <span>Application Runtime Sandbox</span></div>
626
+ <div class="logo">
627
+ Endtester
628
+ <span>Application Runtime Sandbox</span>
629
+ </div>
630
+
89
631
  <div class="header-right">
632
+
90
633
  <div class="base-url-wrap">
91
634
  <label>TARGET HOST</label>
92
- <input id="base-url" type="text" value="">
635
+
636
+ <input
637
+ id="base-url"
638
+ type="text"
639
+ value=""
640
+ />
93
641
  </div>
642
+
94
643
  <div class="jwt-wrap">
95
644
  <label>BEARER AUTH</label>
96
- <input id="jwt-input" type="text" placeholder="Token value...">
645
+
646
+ <input
647
+ id="jwt-input"
648
+ type="text"
649
+ placeholder="Token value..."
650
+ />
97
651
  </div>
652
+
98
653
  </div>
99
654
  </header>
100
655
 
101
656
  <div class="layout">
657
+
102
658
  <aside id="sidebar-nav">
103
- <div class="section-label">Discovered Endpoints</div>
659
+ <div class="section-label">
660
+ Discovered Endpoints
661
+ </div>
104
662
  </aside>
663
+
105
664
  <main id="main-panel"></main>
665
+
106
666
  <div class="response-panel">
667
+
107
668
  <div class="response-header">
108
- <span class="response-header-title">Response Output</span>
109
- <span id="status-badge" class="status-badge status-idle">—</span>
669
+ <span class="response-header-title">
670
+ Response Output
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
110
686
  </div>
111
- <div class="response-body empty" id="response-body">Execute a request row to generate feedback data</div>
687
+
112
688
  </div>
689
+
113
690
  </div>
114
691
 
115
692
  <div id="toast"></div>
116
693
 
117
694
  <script>
118
- const rawPayload = document.getElementById('endtester-data-vault').getAttribute('data-payload');
119
- const ENDPOINTS = JSON.parse(atob(rawPayload));
695
+ const rawPayload =
696
+ document
697
+ .getElementById('endtester-data-vault')
698
+ .getAttribute('data-payload');
699
+
700
+ const ENDPOINTS =
701
+ JSON.parse(atob(rawPayload));
120
702
 
121
703
  let currentEp = '';
122
704
 
123
- document.getElementById('base-url').value = window.location.origin;
705
+ document
706
+ .getElementById('base-url')
707
+ .value = window.location.origin;
124
708
 
125
- function buildSidebar() {
126
- const sidebar = document.getElementById('sidebar-nav');
127
- const keys = Object.keys(ENDPOINTS);
709
+ function buildSidebar(){
710
+
711
+ const sidebar =
712
+ document.getElementById('sidebar-nav');
713
+
714
+ const keys =
715
+ Object.keys(ENDPOINTS);
716
+
717
+ if(keys.length === 0){
718
+
719
+ sidebar.innerHTML +=
720
+ '<div style="padding:15px;color:var(--text-dim)">No endpoints discovered.</div>';
128
721
 
129
- if (keys.length === 0) {
130
- sidebar.innerHTML += '<div style="padding:15px; color:var(--text-dim)">No active application endpoints discovered.</div>';
131
722
  return;
132
723
  }
133
724
 
134
- keys.forEach((key, index) => {
725
+ keys.forEach((key,index)=>{
726
+
135
727
  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'));
728
+
729
+ const div =
730
+ document.createElement('div');
731
+
732
+ div.className =
733
+ index === 0
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
+
142
753
  div.classList.add('active');
754
+
143
755
  clearResponse();
756
+
144
757
  renderPanel(key);
145
758
  });
759
+
146
760
  sidebar.appendChild(div);
147
761
  });
148
762
 
149
- if (keys.length > 0) renderPanel(keys[0]);
763
+ if(keys.length > 0){
764
+ renderPanel(keys[0]);
765
+ }
150
766
  }
151
767
 
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 + ' />';
768
+ function makeInputString(
769
+ type,
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
+ \`;
156
793
  }
157
794
 
158
- function renderPanel(epKey) {
795
+ function renderPanel(epKey){
796
+
159
797
  currentEp = epKey;
798
+
160
799
  const ep = ENDPOINTS[epKey];
161
- const main = document.getElementById('main-panel');
162
- if (!ep) return;
800
+
801
+ const main =
802
+ document.getElementById('main-panel');
803
+
804
+ if(!ep) return;
163
805
 
164
806
  let html = \`
165
- <div class="endpoint-title">\${ep.title}</div>
807
+ <div class="endpoint-title">
808
+ \${ep.title}
809
+ </div>
810
+
166
811
  <div class="endpoint-path">
167
- <span class="method-badge \${ep.method}">\${ep.method}</span>
168
- <span>\${ep.path}</span>
812
+ <span class="method-badge \${ep.method}">
813
+ \${ep.method}
814
+ </span>
815
+
816
+ <span>
817
+ \${ep.path}
818
+ </span>
819
+ </div>
820
+
821
+ <div class="endpoint-desc">
822
+ \${ep.desc}
169
823
  </div>
170
- <div class="endpoint-desc">\${ep.desc}</div>
171
824
  \`;
172
825
 
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, '');
826
+ if(ep.params && ep.params.length){
827
+
828
+ html += \`
829
+ <div class="form-section">
830
+
831
+ <div class="form-section-title">
832
+ Path Parameters
833
+ </div>
834
+ \`;
835
+
836
+ ep.params.forEach((p)=>{
837
+
178
838
  html += \`
179
839
  <div class="field-row">
180
- <label class="field-label">\${p.label}</label>
181
- \${inputHtml}
840
+
841
+ <label class="field-label">
842
+ \${p.label}
843
+ </label>
844
+
845
+ \${makeInputString(
846
+ 'text',
847
+ 'param-' + p.name,
848
+ p.placeholder,
849
+ ''
850
+ )}
851
+
182
852
  </div>
183
853
  \`;
184
854
  });
185
- html += \`</div>\`;
855
+
856
+ html += '</div>';
186
857
  }
187
858
 
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 || '', '');
859
+ if(ep.fields && ep.fields.length){
860
+
861
+ html += \`
862
+ <div class="form-section">
863
+
864
+ <div class="form-section-title">
865
+ Request Body
866
+ </div>
867
+ \`;
868
+
869
+ ep.fields.forEach((f)=>{
870
+
193
871
  html += \`
194
872
  <div class="field-row">
195
- <label class="field-label">\--\${f.label}</label>
196
- \${inputHtml}
873
+
874
+ <label class="field-label">
875
+ \${f.label}
876
+ </label>
877
+
878
+ \${makeInputString(
879
+ f.type || 'text',
880
+ 'field-' + f.name,
881
+ f.placeholder || '',
882
+ ''
883
+ )}
884
+
197
885
  </div>
198
886
  \`;
199
887
  });
200
- html += \`</div>\`;
888
+
889
+ html += '</div>';
201
890
  }
202
891
 
203
892
  html += \`
204
893
  <div class="btn-row">
205
- <button class="btn" onclick="sendRequest()">Execute Route</button>
206
- <button class="btn btn-secondary" onclick="clearResponse()">Clear Context</button>
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
+
207
909
  </div>
208
910
  \`;
209
911
 
210
912
  main.innerHTML = html;
211
913
  }
212
914
 
213
- async function sendRequest() {
214
- const ep = ENDPOINTS[currentEp];
915
+ async function sendRequest(){
916
+
917
+ const ep =
918
+ ENDPOINTS[currentEp];
919
+
215
920
  let path = ep.path;
216
921
 
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));
922
+ if(ep.params){
923
+
924
+ for(const p of ep.params){
925
+
926
+ const val =
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
+ );
223
946
  }
224
947
  }
225
948
 
226
- const baseUrl = document.getElementById('base-url').value.replace(/[/]+$/, '');
949
+ const baseUrl =
950
+ document
951
+ .getElementById('base-url')
952
+ .value
953
+ .replace(/[/]+$/,'');
954
+
227
955
  const url = baseUrl + path;
228
- const headers = { 'Content-Type': 'application/json' };
229
956
 
230
- const jwt = document.getElementById('jwt-input').value.trim();
231
- if (jwt) headers['Authorization'] = 'Bearer ' + jwt;
957
+ const headers = {
958
+ 'Content-Type':'application/json'
959
+ };
960
+
961
+ const jwt =
962
+ document
963
+ .getElementById('jwt-input')
964
+ .value
965
+ .trim();
966
+
967
+ if(jwt){
968
+ headers['Authorization'] =
969
+ 'Bearer ' + jwt;
970
+ }
971
+
972
+ let body;
973
+
974
+ if(
975
+ ep.fields &&
976
+ ep.fields.length &&
977
+ ['POST','PUT','PATCH']
978
+ .includes(ep.method)
979
+ ){
232
980
 
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
981
  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;
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);
247
999
  }
248
- }
1000
+
1001
+ payloadObject[f.name] = value;
1002
+ });
1003
+
249
1004
  body = JSON.stringify(payloadObject);
250
1005
  }
251
1006
 
252
- setResponse(null, 'loading');
1007
+ setResponse(null,'loading');
1008
+
253
1009
  const start = Date.now();
254
1010
 
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();
1011
+ try{
1012
+
1013
+ const res =
1014
+ await fetch(url,{
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
+
259
1026
  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);
1027
+
1028
+ try{
1029
+ json = JSON.parse(text);
1030
+ }catch{
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
+ );
264
1049
  }
265
1050
  }
266
1051
 
267
- function setResponse(data, state, status, ms) {
268
- const badge = document.getElementById('status-badge');
269
- const body = document.getElementById('response-body');
1052
+ function setResponse(
1053
+ data,
1054
+ state,
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';
270
1073
 
271
- if (state === 'loading') {
272
- badge.className = 'status-badge status-idle';
273
1074
  badge.textContent = '...';
274
- body.innerHTML = 'Executing transmission...';
1075
+
1076
+ body.innerHTML =
1077
+ 'Executing transmission...';
1078
+
275
1079
  return;
276
1080
  }
277
1081
 
278
- badge.className = 'status-badge ' + (state === 'ok' ? 'status-ok' : 'status-err');
279
- badge.textContent = status + ' · ' + ms + 'ms';
1082
+ badge.className =
1083
+ 'status-badge ' +
1084
+ (
1085
+ state === 'ok'
1086
+ ? 'status-ok'
1087
+ : 'status-err'
1088
+ );
1089
+
1090
+ badge.textContent =
1091
+ status + ' · ' + ms + 'ms';
1092
+
280
1093
  body.className = 'response-body';
281
- body.innerHTML = highlightJson(typeof data === 'string' ? data : JSON.stringify(data, null, 2));
1094
+
1095
+ body.innerHTML =
1096
+ highlightJson(
1097
+ typeof data === 'string'
1098
+ ? data
1099
+ : JSON.stringify(data,null,2)
1100
+ );
282
1101
  }
283
1102
 
284
- function clearResponse() {
285
- const badge = document.getElementById('status-badge');
286
- const body = document.getElementById('response-body');
287
- badge.className = 'status-badge status-idle';
1103
+ function clearResponse(){
1104
+
1105
+ const badge =
1106
+ document.getElementById(
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
+
288
1118
  badge.textContent = '—';
289
- body.className = 'response-body empty';
290
- body.textContent = 'Execute a request row to generate feedback data';
1119
+
1120
+ body.className =
1121
+ 'response-body empty';
1122
+
1123
+ body.textContent =
1124
+ 'Execute a request row to generate feedback data';
291
1125
  }
292
1126
 
293
- function highlightJson(str) {
1127
+ function highlightJson(str){
1128
+
294
1129
  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>';
1130
+ .replace(/&/g,'&amp;')
1131
+ .replace(/[<]/g,'&lt;')
1132
+ .replace(/[>]/g,'&gt;')
1133
+
1134
+ .replace(
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>';
300
1149
  }
301
- return '<span class="json-num">' + match + '</span>';
302
- });
1150
+ );
303
1151
  }
304
1152
 
305
- function showToast(msg) {
306
- const t = document.getElementById('toast');
1153
+ function showToast(msg){
1154
+
1155
+ const t =
1156
+ document.getElementById('toast');
1157
+
307
1158
  t.textContent = msg;
1159
+
308
1160
  t.classList.add('show');
309
- setTimeout(() => t.classList.remove('show'), 2500);
1161
+
1162
+ setTimeout(()=>{
1163
+ t.classList.remove('show');
1164
+ },2500);
310
1165
  }
311
1166
 
312
1167
  buildSidebar();
313
1168
  </script>
1169
+
314
1170
  </body>
315
1171
  </html>
316
1172
  `;