@vnphu/nestjs-api-explorer 0.2.0 → 0.2.2
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-explorer.html.d.ts","sourceRoot":"","sources":["../src/api-explorer.html.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AAEnE,wBAAgB,eAAe,CAAC,OAAO,EAAE,0BAA0B,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"api-explorer.html.d.ts","sourceRoot":"","sources":["../src/api-explorer.html.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AAEnE,wBAAgB,eAAe,CAAC,OAAO,EAAE,0BAA0B,GAAG,MAAM,CAqnD3E"}
|
|
@@ -254,7 +254,7 @@ function getExplorerHtml(options) {
|
|
|
254
254
|
|
|
255
255
|
/* ── Summary Panel ── */
|
|
256
256
|
#summary-panel {
|
|
257
|
-
width:
|
|
257
|
+
width: 400px; flex-shrink: 0; border-left: 1px solid var(--border);
|
|
258
258
|
background: var(--bg-white); display: flex; flex-direction: column; overflow: hidden;
|
|
259
259
|
}
|
|
260
260
|
#summary-panel.hidden { display: none; }
|
|
@@ -263,6 +263,13 @@ function getExplorerHtml(options) {
|
|
|
263
263
|
display: flex; align-items: center; justify-content: space-between;
|
|
264
264
|
}
|
|
265
265
|
.summary-header-title { font-size: 11px; font-weight: 700; color: var(--text); text-transform: uppercase; letter-spacing: 0.5px; }
|
|
266
|
+
.summary-copy-btn {
|
|
267
|
+
display: flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 500;
|
|
268
|
+
color: var(--text-muted); background: none; border: 1px solid var(--border);
|
|
269
|
+
border-radius: var(--radius-sm); padding: 3px 8px; cursor: pointer; transition: all 0.15s;
|
|
270
|
+
}
|
|
271
|
+
.summary-copy-btn:hover { background: var(--bg-input); color: var(--accent); border-color: var(--accent); }
|
|
272
|
+
.summary-copy-btn.copied { color: #16a34a; border-color: #86efac; background: #f0fdf4; }
|
|
266
273
|
#summary-body { flex: 1; overflow-y: auto; padding: 10px 14px 16px; display: flex; flex-direction: column; gap: 14px; }
|
|
267
274
|
.summary-section { display: flex; flex-direction: column; gap: 6px; }
|
|
268
275
|
.summary-section-label {
|
|
@@ -607,6 +614,9 @@ function getExplorerHtml(options) {
|
|
|
607
614
|
<path d="M7 11V7a5 5 0 0 1 10 0v4"/>
|
|
608
615
|
</svg>
|
|
609
616
|
</button>
|
|
617
|
+
<a href="https://github.com/vnphu/nestjs-api-explorer" target="_blank" rel="noopener" class="icon-btn" title="View on GitHub" style="display:inline-flex;align-items:center;justify-content:center;text-decoration:none">
|
|
618
|
+
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z"/></svg>
|
|
619
|
+
</a>
|
|
610
620
|
<button class="icon-btn" id="reload-btn" title="Reload routes">
|
|
611
621
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
612
622
|
<polyline points="23 4 23 10 17 10"/>
|
|
@@ -829,6 +839,10 @@ function getExplorerHtml(options) {
|
|
|
829
839
|
<div id="summary-panel" class="hidden">
|
|
830
840
|
<div class="summary-header">
|
|
831
841
|
<span class="summary-header-title">Request Summary</span>
|
|
842
|
+
<button id="summary-copy-btn" class="summary-copy-btn hidden" title="Copy summary">
|
|
843
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
|
844
|
+
<span id="summary-copy-label">Copy</span>
|
|
845
|
+
</button>
|
|
832
846
|
</div>
|
|
833
847
|
<div id="summary-body">
|
|
834
848
|
<div class="summary-empty" id="summary-empty">
|
|
@@ -928,11 +942,9 @@ function groupRoutes(routes) {
|
|
|
928
942
|
|
|
929
943
|
function renderRouteItem(r) {
|
|
930
944
|
const active = S.selected?.method === r.method && S.selected?.path === r.path ? ' active' : '';
|
|
931
|
-
const desc = r.description ? \`<span class="route-desc">\${esc(r.description.split('\\n')[0])}</span>\` : '';
|
|
932
945
|
return \`<div class="route-item\${active}" data-method="\${r.method}" data-path="\${esc(r.path)}">
|
|
933
946
|
<span class="method-badge method-\${r.method}">\${r.method}</span>
|
|
934
947
|
<span class="route-path" title="\${esc(r.path)}">\${esc(r.path)}</span>
|
|
935
|
-
\${desc}
|
|
936
948
|
</div>\`;
|
|
937
949
|
}
|
|
938
950
|
|
|
@@ -1011,8 +1023,28 @@ function selectRoute(route) {
|
|
|
1011
1023
|
document.querySelectorAll('#req-panel-inner .tab-panel').forEach(p => p.classList.remove('active'));
|
|
1012
1024
|
document.getElementById('tab-params').classList.add('active');
|
|
1013
1025
|
|
|
1014
|
-
// Auto-
|
|
1026
|
+
// Auto-fill body editor from body schema if available
|
|
1015
1027
|
const bodyMethods = ['POST', 'PUT', 'PATCH'];
|
|
1028
|
+
if (bodyMethods.includes(route.method) && route.body?.length) {
|
|
1029
|
+
const template = {};
|
|
1030
|
+
route.body.forEach(f => {
|
|
1031
|
+
if (f.type === 'number') template[f.name] = 0;
|
|
1032
|
+
else if (f.type === 'boolean') template[f.name] = false;
|
|
1033
|
+
else if (f.type === 'array') template[f.name] = [];
|
|
1034
|
+
else if (f.type === 'object') template[f.name] = {};
|
|
1035
|
+
else template[f.name] = '';
|
|
1036
|
+
});
|
|
1037
|
+
S.body.content = JSON.stringify(template, null, 2);
|
|
1038
|
+
S.body.type = 'json';
|
|
1039
|
+
el.bodyEditor.value = S.body.content;
|
|
1040
|
+
// Sync body type radio
|
|
1041
|
+
document.querySelectorAll('[name="body-type"]').forEach(r => {
|
|
1042
|
+
r.checked = r.value === 'json';
|
|
1043
|
+
});
|
|
1044
|
+
el.formatBtn.style.display = '';
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
// Auto-switch to Body tab for methods that send a body
|
|
1016
1048
|
if (bodyMethods.includes(route.method) && !route.params.length) {
|
|
1017
1049
|
el.reqTabBar.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
|
1018
1050
|
el.reqTabBar.querySelector('[data-tab="body"]').classList.add('active');
|
|
@@ -1198,16 +1230,21 @@ function renderSummary() {
|
|
|
1198
1230
|
const empty = $('summary-empty');
|
|
1199
1231
|
const content = $('summary-content');
|
|
1200
1232
|
|
|
1233
|
+
const copyBtn = $('summary-copy-btn');
|
|
1234
|
+
|
|
1201
1235
|
if (!S.selected) {
|
|
1202
1236
|
panel.classList.remove('hidden');
|
|
1203
1237
|
empty.style.display = 'flex';
|
|
1204
|
-
content.
|
|
1238
|
+
content.classList.add('hidden');
|
|
1239
|
+
copyBtn.classList.add('hidden');
|
|
1205
1240
|
return;
|
|
1206
1241
|
}
|
|
1207
1242
|
|
|
1208
1243
|
panel.classList.remove('hidden');
|
|
1209
1244
|
empty.style.display = 'none';
|
|
1245
|
+
content.classList.remove('hidden');
|
|
1210
1246
|
content.style.display = 'flex';
|
|
1247
|
+
copyBtn.classList.remove('hidden');
|
|
1211
1248
|
|
|
1212
1249
|
const method = S.selected.method;
|
|
1213
1250
|
const fullUrl = buildUrl();
|
|
@@ -1320,6 +1357,65 @@ function renderSummary() {
|
|
|
1320
1357
|
\`;
|
|
1321
1358
|
}
|
|
1322
1359
|
|
|
1360
|
+
// ── Copy Summary ───────────────────────────────────────────────────
|
|
1361
|
+
function copySummary() {
|
|
1362
|
+
if (!S.selected) return;
|
|
1363
|
+
const r = S.selected;
|
|
1364
|
+
const lines = [];
|
|
1365
|
+
|
|
1366
|
+
lines.push(\`\${r.method} \${buildUrl()}\`);
|
|
1367
|
+
if (r.description) lines.push(\`\`, ...r.description.split('\\n').map(l => \`# \${l}\`));
|
|
1368
|
+
|
|
1369
|
+
if (r.params?.length) {
|
|
1370
|
+
lines.push(\`\`, \`[Path Params]\`);
|
|
1371
|
+
r.params.forEach(p => lines.push(\` \${p}: \${S.pathParams[p] || ''}\`));
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
const activeQuery = S.queryParams.filter(p => p.enabled && p.key);
|
|
1375
|
+
if (activeQuery.length) {
|
|
1376
|
+
lines.push(\`\`, \`[Query Params]\`);
|
|
1377
|
+
activeQuery.forEach(p => lines.push(\` \${p.key}: \${p.value}\`));
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
if (r.query?.length) {
|
|
1381
|
+
lines.push(\`\`, \`[Query Schema]\`);
|
|
1382
|
+
r.query.forEach(f => lines.push(\` \${f.name}: \${f.type}\${f.required ? ' (required)' : ''}\${f.rules?.length ? ' | ' + f.rules.join(', ') : ''}\${f.description ? ' — ' + f.description : ''}\`));
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
const activeHeaders = S.reqHeaders.filter(h => h.enabled && h.key);
|
|
1386
|
+
if (activeHeaders.length) {
|
|
1387
|
+
lines.push(\`\`, \`[Headers]\`);
|
|
1388
|
+
activeHeaders.forEach(h => lines.push(\` \${h.key}: \${h.value}\`));
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
if (r.headers?.length) {
|
|
1392
|
+
lines.push(\`\`, \`[Required Headers]\`);
|
|
1393
|
+
r.headers.forEach(f => lines.push(\` \${f.name}\${f.required ? ' (required)' : ''}\${f.description ? ' — ' + f.description : ''}\`));
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
if (r.body?.length) {
|
|
1397
|
+
lines.push(\`\`, \`[Body Schema]\`);
|
|
1398
|
+
r.body.forEach(f => lines.push(\` \${f.name}: \${f.type}\${f.required ? ' (required)' : ' (optional)'}\${f.rules?.length ? ' | ' + f.rules.join(', ') : ''}\${f.description ? ' — ' + f.description : ''}\`));
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
if (['POST','PUT','PATCH'].includes(r.method) && S.body.type !== 'none' && S.body.content) {
|
|
1402
|
+
lines.push(\`\`, \`[Body Content (\${S.body.type})]\`, S.body.content);
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
if (r.response?.length) {
|
|
1406
|
+
lines.push(\`\`, \`[Response Schema]\`);
|
|
1407
|
+
r.response.forEach(f => lines.push(\` \${f.name}: \${f.type}\${f.description ? ' — ' + f.description : ''}\`));
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
navigator.clipboard.writeText(lines.join('\\n')).then(() => {
|
|
1411
|
+
const btn = $('summary-copy-btn');
|
|
1412
|
+
const label = $('summary-copy-label');
|
|
1413
|
+
btn.classList.add('copied');
|
|
1414
|
+
label.textContent = 'Copied!';
|
|
1415
|
+
setTimeout(() => { btn.classList.remove('copied'); label.textContent = 'Copy'; }, 1800);
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1323
1419
|
// Render a list of DocField items (body/query/headers/response) in the summary panel
|
|
1324
1420
|
function renderDocFields(label, fields) {
|
|
1325
1421
|
if (!fields || fields.length === 0) return '';
|
|
@@ -1487,6 +1583,8 @@ function bindEvents() {
|
|
|
1487
1583
|
el.baseUrl.addEventListener('input', () => { renderUrlBar(); renderSummary(); });
|
|
1488
1584
|
el.sendBtn.addEventListener('click', sendRequest);
|
|
1489
1585
|
|
|
1586
|
+
$('summary-copy-btn').addEventListener('click', copySummary);
|
|
1587
|
+
|
|
1490
1588
|
el.addQueryBtn.addEventListener('click', () => {
|
|
1491
1589
|
S.queryParams.push({ id: uid(), key: '', value: '', enabled: true });
|
|
1492
1590
|
renderQueryParams();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-explorer.html.js","sourceRoot":"","sources":["../src/api-explorer.html.ts"],"names":[],"mappings":";;AAEA,
|
|
1
|
+
{"version":3,"file":"api-explorer.html.js","sourceRoot":"","sources":["../src/api-explorer.html.ts"],"names":[],"mappings":";;AAEA,0CAqnDC;AArnDD,SAAgB,eAAe,CAAC,OAAmC;IACjE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IAChC,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAE/C,OAAO,UAAU,CAAC;;;;;WAKT,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAi1BC,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA0xBf,CAAC;AACT,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vnphu/nestjs-api-explorer",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "An in-app API explorer for NestJS — like Postman, but built right into your app.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -25,6 +25,14 @@
|
|
|
25
25
|
],
|
|
26
26
|
"author": "vnphu",
|
|
27
27
|
"license": "MIT",
|
|
28
|
+
"homepage": "https://github.com/vnphu/nestjs-api-explorer#readme",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/vnphu/nestjs-api-explorer.git"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/vnphu/nestjs-api-explorer/issues"
|
|
35
|
+
},
|
|
28
36
|
"peerDependencies": {
|
|
29
37
|
"@nestjs/common": ">=9.0.0",
|
|
30
38
|
"@nestjs/core": ">=9.0.0",
|