@aimeloic/monkey-tester 1.0.10 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/htmlTemplate.js CHANGED
@@ -1,307 +1,1172 @@
1
1
  export function getHtmlTemplate(endpoints) {
2
- // Safe stringification for HTML attribute encoding to prevent script engine parsing breaks
3
- const safeJsonString = Buffer.from(JSON.stringify(endpoints)).toString('base64');
2
+ const safeJsonString = Buffer
3
+ .from(JSON.stringify(endpoints))
4
+ .toString('base64');
4
5
 
5
6
  return `
6
7
  <!DOCTYPE html>
7
8
  <html lang="en">
8
9
  <head>
9
- <meta charset="UTF-8">
10
- <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
+
11
16
  <title>Endtester — API Environment</title>
12
- <link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700&family=DM+Mono:wght@400;500&family=DM+Sans:wght@300;400;500&display=swap" rel="stylesheet">
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
+
13
23
  <style>
14
- :root {
15
- --bg: #0e0c09;
16
- --surface: #181510;
17
- --surface2: #221d14;
18
- --border: #3a3020;
19
- --accent: #e8a838;
20
- --accent2: #c47a1e;
21
- --text: #f0e8d8;
22
- --text-dim: #9a8c78;
23
- --red: #d45c3c;
24
- --green: #6ba05a;
25
- --blue: #5a86c0;
26
- --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;
27
600
  }
28
- * { box-sizing: border-box; margin: 0; padding: 0; }
29
- body {
30
- background: var(--bg); color: var(--text); font-family: 'DM Sans', sans-serif; font-size: 14px; min-height: 100vh;
31
- background-image: radial-gradient(ellipse 80% 60% at 50% -20%, #3a2a0a22 0%, transparent 70%);
601
+
602
+ aside{
603
+ display:none;
32
604
  }
33
- header {
34
- border-bottom: 1px solid var(--border); padding: 20px 32px; display: flex; align-items: center; gap: 20px;
35
- background: #0e0c09ee; backdrop-filter: blur(8px); position: sticky; top: 0; z-index: 100;
605
+
606
+ .field-row{
607
+ grid-template-columns:1fr;
36
608
  }
37
- .logo { font-family: 'Playfair Display', serif; font-size: 22px; color: var(--accent); letter-spacing: 0.02em; }
38
- .logo span { color: var(--text-dim); font-size: 13px; font-family: 'DM Mono', monospace; display: block; font-weight: 400; }
39
- .header-right { margin-left: auto; display: flex; align-items: center; gap: 12px; }
40
- .jwt-wrap, .base-url-wrap { display: flex; align-items: center; gap: 8px; }
41
- .jwt-wrap label, .base-url-wrap label { color: var(--text-dim); font-size: 12px; font-family: 'DM Mono', monospace; }
42
- #jwt-input, #base-url {
43
- background: var(--surface2); border: 1px solid var(--border); color: var(--text);
44
- 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;
45
612
  }
46
- .layout { display: grid; grid-template-columns: 260px 1fr 400px; height: calc(100vh - 65px); }
47
- aside { border-right: 1px solid var(--border); overflow-y: auto; padding: 16px 0; }
48
- .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; }
49
- .nav-item { display: flex; align-items: center; gap: 10px; padding: 8px 18px; cursor: pointer; border-left: 2px solid transparent; color: var(--text-dim); }
50
- .nav-item:hover { background: var(--surface); color: var(--text); }
51
- .nav-item.active { border-left-color: var(--accent); background: var(--surface); color: var(--accent); }
52
- .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; }
53
- .GET { background: #1a3a22; color: #6ba05a; }
54
- .POST { background: #1a2e3a; color: #5a86c0; }
55
- .PUT, .PATCH { background: #3a2e10; color: #e8a838; }
56
- .DELETE { background: #3a1a14; color: #d45c3c; }
57
- .nav-label { font-size: 11px; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
58
- main { overflow-y: auto; padding: 24px; }
59
- .endpoint-title { font-family: 'Playfair Display', serif; font-size: 20px; color: var(--accent); margin-bottom: 6px; }
60
- .endpoint-path { font-family: 'DM Mono', monospace; font-size: 12px; color: var(--text-dim); margin-bottom: 20px; display: flex; align-items: center; gap: 8px; }
61
- .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; }
62
- .form-section { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px; margin-bottom: 16px; }
63
- .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; }
64
- .field-row { display: grid; grid-template-columns: 140px 1fr; align-items: center; gap: 10px; margin-bottom: 10px; }
65
- .field-label { font-family: 'DM Mono', monospace; font-size: 11px; color: var(--text-dim); text-align: right; }
66
- 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; }
67
- .btn { background: var(--accent); color: #0e0c09; border: none; padding: 10px 22px; border-radius: var(--radius); font-size: 13px; font-weight: 500; cursor: pointer; }
68
- .btn-secondary { background: var(--surface2); color: var(--text-dim); border: 1px solid var(--border); margin-left: 8px; }
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>
110
679
  </div>
111
- <div class="response-body empty" id="response-body">Execute a request row to generate feedback data</div>
680
+
681
+ <div
682
+ class="response-body empty"
683
+ id="response-body"
684
+ >
685
+ Execute a request row to generate feedback data
686
+ </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;
708
+
709
+ function buildSidebar(){
124
710
 
125
- function buildSidebar() {
126
- const sidebar = document.getElementById('sidebar-nav');
127
- const keys = Object.keys(ENDPOINTS);
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;
163
800
 
164
- // FIXED: Escaped template strings (\`) prevent string concatenation mismatch errors
801
+ const main =
802
+ document.getElementById('main-panel');
803
+
804
+ if(!ep) return;
805
+
165
806
  let html = \`
166
- <div class="endpoint-title">\${ep.title}</div>
807
+ <div class="endpoint-title">
808
+ \${ep.title}
809
+ </div>
810
+
167
811
  <div class="endpoint-path">
168
- <span class="method-badge \${ep.method}">\${ep.method}</span>
169
- <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}
170
823
  </div>
171
- <div class="endpoint-desc">\${ep.desc}</div>
172
824
  \`;
173
825
 
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
- if (ep.fields && ep.fields.length) {
189
- html += \`<div class="form-section"><div class="form-section-title">JSON Request Body Raw Payload</div>\`;
190
- ep.fields.forEach(function(f) {
191
- const inputHtml = makeInputString('text', 'field-' + f.name, '', '{"key": "value"}');
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
+
192
871
  html += \`
193
872
  <div class="field-row">
194
- <label class="field-label">\${f.label}</label>
195
- \${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
+
196
885
  </div>
197
886
  \`;
198
887
  });
199
- html += \`</div>\`;
888
+
889
+ html += '</div>';
200
890
  }
201
891
 
202
892
  html += \`
203
893
  <div class="btn-row">
204
- <button class="btn" onclick="sendRequest()">Execute Route</button>
205
- <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
+
206
909
  </div>
207
910
  \`;
208
911
 
209
912
  main.innerHTML = html;
210
913
  }
211
914
 
212
- async function sendRequest() {
213
- const ep = ENDPOINTS[currentEp];
915
+ async function sendRequest(){
916
+
917
+ const ep =
918
+ ENDPOINTS[currentEp];
919
+
214
920
  let path = ep.path;
215
921
 
216
- if (ep.params) {
217
- for (const p of ep.params) {
218
- const val = document.getElementById('param-' + p.name)?.value.trim();
219
- if (!val) { showToast('Warning: Parameter ' + p.label + ' is required'); return; }
220
- 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
+ );
221
946
  }
222
947
  }
223
948
 
224
- const baseUrl = document.getElementById('base-url').value.replace(/[/]+$/, '');
949
+ const baseUrl =
950
+ document
951
+ .getElementById('base-url')
952
+ .value
953
+ .replace(/[/]+$/,'');
954
+
225
955
  const url = baseUrl + path;
226
- const headers = { 'Content-Type': 'application/json' };
227
-
228
- const jwt = document.getElementById('jwt-input').value.trim();
229
- if (jwt) headers['Authorization'] = 'Bearer ' + jwt;
230
-
231
- let body = undefined;
232
- if (ep.fields && ep.fields.length && ['POST', 'PUT', 'PATCH'].includes(ep.method)) {
233
- const rawVal = document.getElementById('field-' + ep.fields[0].name)?.value?.trim() ?? '';
234
- try {
235
- JSON.parse(rawVal);
236
- body = rawVal;
237
- } catch(e) {
238
- showToast('Malformed JSON body structure provided.');
239
- return;
240
- }
956
+
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
+ ){
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);
999
+ }
1000
+
1001
+ payloadObject[f.name] = value;
1002
+ });
1003
+
1004
+ body = JSON.stringify(payloadObject);
241
1005
  }
242
1006
 
243
- setResponse(null, 'loading');
1007
+ setResponse(null,'loading');
1008
+
244
1009
  const start = Date.now();
245
1010
 
246
- try {
247
- const res = await fetch(url, { method: ep.method, headers, body });
248
- const ms = Date.now() - start;
249
- 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
+
250
1026
  let json;
251
- try { json = JSON.parse(text); } catch { json = text; }
252
- setResponse(json, res.ok ? 'ok' : 'err', res.status, ms);
253
- } catch (err) {
254
- 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
+ );
255
1049
  }
256
1050
  }
257
1051
 
258
- function setResponse(data, state, status, ms) {
259
- const badge = document.getElementById('status-badge');
260
- 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';
261
1073
 
262
- if (state === 'loading') {
263
- badge.className = 'status-badge status-idle';
264
1074
  badge.textContent = '...';
265
- body.innerHTML = 'Executing transmission...';
1075
+
1076
+ body.innerHTML =
1077
+ 'Executing transmission...';
1078
+
266
1079
  return;
267
1080
  }
268
1081
 
269
- badge.className = 'status-badge ' + (state === 'ok' ? 'status-ok' : 'status-err');
270
- 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
+
271
1093
  body.className = 'response-body';
272
- 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
+ );
273
1101
  }
274
1102
 
275
- function clearResponse() {
276
- const badge = document.getElementById('status-badge');
277
- const body = document.getElementById('response-body');
278
- 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
+
279
1118
  badge.textContent = '—';
280
- body.className = 'response-body empty';
281
- 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';
282
1125
  }
283
1126
 
284
- function highlightJson(str) {
1127
+ function highlightJson(str){
1128
+
285
1129
  return str
286
- .replace(/&/g, '&amp;').replace(/[<]/g, '&lt;').replace(/[>]/g, '&gt;')
287
- .replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function(match) {
288
- if (/^"/.test(match)) {
289
- if (/:$/.test(match)) return '<span class="json-key">' + match + '</span>';
290
- 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>';
291
1149
  }
292
- return '<span class="json-num">' + match + '</span>';
293
- });
1150
+ );
294
1151
  }
295
1152
 
296
- function showToast(msg) {
297
- const t = document.getElementById('toast');
1153
+ function showToast(msg){
1154
+
1155
+ const t =
1156
+ document.getElementById('toast');
1157
+
298
1158
  t.textContent = msg;
1159
+
299
1160
  t.classList.add('show');
300
- setTimeout(() => t.classList.remove('show'), 2500);
1161
+
1162
+ setTimeout(()=>{
1163
+ t.classList.remove('show');
1164
+ },2500);
301
1165
  }
302
1166
 
303
1167
  buildSidebar();
304
1168
  </script>
1169
+
305
1170
  </body>
306
1171
  </html>
307
1172
  `;