@emeryld/rrroutes-contract 2.2.2 → 2.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +462 -160
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +462 -160
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -489,7 +489,7 @@ var CSS_STYLES = `:root {
|
|
|
489
489
|
|
|
490
490
|
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
491
491
|
|
|
492
|
-
/* Tag Pastel Palette
|
|
492
|
+
/* Tag Pastel Palette */
|
|
493
493
|
--tag-0-bg: rgba(254, 202, 202, 0.2); --tag-0-fg: #fca5a5; /* Red */
|
|
494
494
|
--tag-1-bg: rgba(253, 230, 138, 0.2); --tag-1-fg: #fcd34d; /* Amber */
|
|
495
495
|
--tag-2-bg: rgba(187, 247, 208, 0.2); --tag-2-fg: #86efac; /* Green */
|
|
@@ -562,6 +562,21 @@ body {
|
|
|
562
562
|
box-shadow: 0 10px 30px -10px rgba(0,0,0,0.5);
|
|
563
563
|
}
|
|
564
564
|
|
|
565
|
+
.schema-indent {
|
|
566
|
+
display: inline-block;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
.schema-branch {
|
|
570
|
+
opacity: 0.6;
|
|
571
|
+
font-family: var(--font-mono);
|
|
572
|
+
margin-right: 2px;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.schema-meta code {
|
|
576
|
+
font-family: var(--font-mono);
|
|
577
|
+
font-size: 11px;
|
|
578
|
+
}
|
|
579
|
+
|
|
565
580
|
/* Top Row: Filters */
|
|
566
581
|
.filters-row {
|
|
567
582
|
display: flex;
|
|
@@ -573,28 +588,27 @@ body {
|
|
|
573
588
|
/* New Wrapper for Search and Methods */
|
|
574
589
|
.left-column {
|
|
575
590
|
display: flex;
|
|
576
|
-
flex-direction: column;
|
|
577
|
-
gap: 16px;
|
|
578
|
-
flex-basis: 300px;
|
|
591
|
+
flex-direction: column;
|
|
592
|
+
gap: 16px;
|
|
593
|
+
flex-basis: 300px;
|
|
579
594
|
min-width: 250px;
|
|
580
595
|
}
|
|
581
596
|
|
|
582
597
|
.search-box {
|
|
583
|
-
|
|
584
|
-
width: 100%; /* Make search fill the left-column width */
|
|
598
|
+
width: 100%;
|
|
585
599
|
position: relative;
|
|
586
600
|
}
|
|
587
601
|
|
|
588
602
|
/* Tag Filter Group */
|
|
589
603
|
.filter-group.tag-filters-container {
|
|
590
|
-
flex: 1;
|
|
604
|
+
flex: 1;
|
|
591
605
|
min-width: 200px;
|
|
592
606
|
}
|
|
593
607
|
|
|
594
608
|
/* Method Filter Group */
|
|
595
609
|
.filter-group.method-filters-container {
|
|
596
|
-
/* Stays inside the left-column, no need for 100% basis */
|
|
597
610
|
}
|
|
611
|
+
|
|
598
612
|
.search-input {
|
|
599
613
|
width: 100%;
|
|
600
614
|
background: rgba(2, 6, 23, 0.6);
|
|
@@ -675,14 +689,21 @@ body {
|
|
|
675
689
|
border-color: var(--accent-primary);
|
|
676
690
|
}
|
|
677
691
|
|
|
678
|
-
/*
|
|
679
|
-
.
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
border-
|
|
692
|
+
/* Tag filter pills */
|
|
693
|
+
.tag-filter-pill {
|
|
694
|
+
display: inline-block;
|
|
695
|
+
padding: 3px 8px;
|
|
696
|
+
border-radius: 4px;
|
|
697
|
+
font-size: 11px;
|
|
698
|
+
font-weight: 600;
|
|
699
|
+
background: rgba(30, 41, 59, 0.5);
|
|
700
|
+
color: var(--text-muted);
|
|
701
|
+
border: 1px solid transparent;
|
|
702
|
+
transition: all 0.15s;
|
|
683
703
|
}
|
|
684
|
-
.pill-checkbox.colored-tag
|
|
685
|
-
|
|
704
|
+
.pill-checkbox.colored-tag input:checked + .tag-filter-pill {
|
|
705
|
+
border-color: var(--accent-primary);
|
|
706
|
+
box-shadow: 0 0 0 1px var(--accent-glow);
|
|
686
707
|
}
|
|
687
708
|
|
|
688
709
|
/* Group Jump Links */
|
|
@@ -762,8 +783,8 @@ body {
|
|
|
762
783
|
padding: 12px 16px;
|
|
763
784
|
display: flex;
|
|
764
785
|
align-items: center;
|
|
765
|
-
gap: 12px;
|
|
766
|
-
flex-wrap: wrap;
|
|
786
|
+
gap: 12px;
|
|
787
|
+
flex-wrap: wrap;
|
|
767
788
|
}
|
|
768
789
|
|
|
769
790
|
/* Item 1: Method */
|
|
@@ -784,7 +805,7 @@ body {
|
|
|
784
805
|
.m-PATCH { background: var(--method-patch-bg); color: var(--method-patch); border: 1px solid rgba(45, 212, 191, 0.3); }
|
|
785
806
|
.m-DELETE { background: var(--method-delete-bg); color: var(--method-delete); border: 1px solid rgba(248, 113, 113, 0.3); }
|
|
786
807
|
|
|
787
|
-
/* Item 2: Path
|
|
808
|
+
/* Item 2: Path */
|
|
788
809
|
.path-container {
|
|
789
810
|
font-family: var(--font-mono);
|
|
790
811
|
font-size: 13px;
|
|
@@ -811,7 +832,7 @@ body {
|
|
|
811
832
|
}
|
|
812
833
|
.path-container:hover .copy-icon { opacity: 1; }
|
|
813
834
|
|
|
814
|
-
/* Item 3: Tags
|
|
835
|
+
/* Item 3: Tags */
|
|
815
836
|
.tags-container {
|
|
816
837
|
display: flex;
|
|
817
838
|
align-items: center;
|
|
@@ -881,6 +902,11 @@ body {
|
|
|
881
902
|
}
|
|
882
903
|
.summary-text { font-size: 14px; color: var(--text-main); line-height: 1.5; }
|
|
883
904
|
|
|
905
|
+
.description-text {
|
|
906
|
+
font-size: 12px;
|
|
907
|
+
opacity: 0.7;
|
|
908
|
+
}
|
|
909
|
+
|
|
884
910
|
/* Tables */
|
|
885
911
|
.schema-table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
|
886
912
|
.schema-table th {
|
|
@@ -896,6 +922,13 @@ body {
|
|
|
896
922
|
.req-true { color: #4ade80; background: rgba(74, 222, 128, 0.1); }
|
|
897
923
|
.req-false { color: #94a3b8; background: rgba(148, 163, 184, 0.1); }
|
|
898
924
|
|
|
925
|
+
.schema-subtitle {
|
|
926
|
+
font-size: 11px;
|
|
927
|
+
font-weight: 600;
|
|
928
|
+
margin-bottom: 4px;
|
|
929
|
+
color: #64748b;
|
|
930
|
+
}
|
|
931
|
+
|
|
899
932
|
.empty-message { text-align: center; padding: 40px; color: var(--text-muted); }
|
|
900
933
|
`;
|
|
901
934
|
var DOCS_JS = `
|
|
@@ -906,7 +939,7 @@ var DOCS_JS = `
|
|
|
906
939
|
} catch(e) { console.error('Failed to parse docs', e); }
|
|
907
940
|
|
|
908
941
|
// State
|
|
909
|
-
|
|
942
|
+
const filters = {
|
|
910
943
|
search: '',
|
|
911
944
|
methods: new Set(),
|
|
912
945
|
tags: new Set()
|
|
@@ -925,14 +958,13 @@ var DOCS_JS = `
|
|
|
925
958
|
// Initialization
|
|
926
959
|
function init() {
|
|
927
960
|
populateFilters();
|
|
928
|
-
render();
|
|
929
961
|
setupGlobalListeners();
|
|
962
|
+
render();
|
|
930
963
|
}
|
|
931
964
|
|
|
932
965
|
// Tag Coloring Logic
|
|
933
966
|
function getTagColorClass(tagName) {
|
|
934
967
|
if (tagName === 'not-implemented') return 'not-implemented';
|
|
935
|
-
// Simple string hash to select color index
|
|
936
968
|
let hash = 0;
|
|
937
969
|
for (let i = 0; i < tagName.length; i++) {
|
|
938
970
|
hash = tagName.charCodeAt(i) + ((hash << 5) - hash);
|
|
@@ -951,55 +983,87 @@ var DOCS_JS = `
|
|
|
951
983
|
}
|
|
952
984
|
|
|
953
985
|
function populateFilters() {
|
|
986
|
+
if (!elMethodFilters || !elTagFilters) return;
|
|
987
|
+
|
|
954
988
|
// Extract unique values
|
|
955
989
|
const allMethods = new Set(leaves.map(l => (l.method || 'GET').toUpperCase()));
|
|
956
990
|
const allTags = new Set(leaves.flatMap(l => (l.cfg && l.cfg.tags) ? l.cfg.tags : []));
|
|
957
991
|
|
|
958
992
|
// Setup Method Checkboxes
|
|
959
993
|
const sortedMethods = Array.from(allMethods).sort();
|
|
960
|
-
elMethodFilters.innerHTML = sortedMethods.map(m =>
|
|
961
|
-
<label class="pill-checkbox">
|
|
962
|
-
<input type="checkbox" value="
|
|
963
|
-
<span
|
|
964
|
-
</label>
|
|
965
|
-
|
|
994
|
+
elMethodFilters.innerHTML = sortedMethods.map(m => (
|
|
995
|
+
'<label class="pill-checkbox">' +
|
|
996
|
+
'<input type="checkbox" value="' + escapeAttr(m) + '" checked>' +
|
|
997
|
+
'<span>' + escapeHtml(m) + '</span>' +
|
|
998
|
+
'</label>'
|
|
999
|
+
)).join('');
|
|
966
1000
|
sortedMethods.forEach(m => filters.methods.add(m));
|
|
967
1001
|
|
|
968
|
-
//
|
|
1002
|
+
// Attach listeners to method checkboxes
|
|
1003
|
+
elMethodFilters.querySelectorAll('input[type="checkbox"]').forEach(function(cb) {
|
|
1004
|
+
cb.addEventListener('change', updateFilters);
|
|
1005
|
+
});
|
|
1006
|
+
|
|
1007
|
+
// Setup Tag Checkboxes
|
|
969
1008
|
if (allTags.size > 0) {
|
|
970
1009
|
elTagFilters.innerHTML = Array.from(allTags).sort().map(t => {
|
|
971
1010
|
const colorClass = getTagColorClass(t);
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
<
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
\`;
|
|
1011
|
+
return (
|
|
1012
|
+
'<label class="pill-checkbox colored-tag">' +
|
|
1013
|
+
'<input type="checkbox" value="' + escapeAttr(t) + '">' +
|
|
1014
|
+
'<span class="tag-filter-pill ' + colorClass + '">' + escapeHtml(t) + '</span>' +
|
|
1015
|
+
'</label>'
|
|
1016
|
+
);
|
|
979
1017
|
}).join('');
|
|
1018
|
+
|
|
1019
|
+
// Attach listeners to tag checkboxes
|
|
1020
|
+
elTagFilters.querySelectorAll('input[type="checkbox"]').forEach(function(cb) {
|
|
1021
|
+
cb.addEventListener('change', updateFilters);
|
|
1022
|
+
});
|
|
980
1023
|
}
|
|
981
1024
|
}
|
|
982
1025
|
|
|
983
|
-
|
|
984
|
-
|
|
1026
|
+
function updateFilters() {
|
|
1027
|
+
if (elSearch) {
|
|
1028
|
+
filters.search = elSearch.value.toLowerCase();
|
|
1029
|
+
}
|
|
1030
|
+
|
|
985
1031
|
filters.methods.clear();
|
|
986
|
-
elMethodFilters
|
|
1032
|
+
if (elMethodFilters) {
|
|
1033
|
+
elMethodFilters.querySelectorAll('input[type="checkbox"]:checked').forEach(function(cb) {
|
|
1034
|
+
filters.methods.add(cb.value);
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
|
|
987
1038
|
filters.tags.clear();
|
|
988
|
-
elTagFilters
|
|
1039
|
+
if (elTagFilters) {
|
|
1040
|
+
elTagFilters.querySelectorAll('input[type="checkbox"]:checked').forEach(function(cb) {
|
|
1041
|
+
filters.tags.add(cb.value);
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
|
|
989
1045
|
render();
|
|
990
1046
|
}
|
|
991
1047
|
|
|
1048
|
+
function setupGlobalListeners() {
|
|
1049
|
+
if (elSearch) {
|
|
1050
|
+
elSearch.addEventListener('input', updateFilters);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
992
1054
|
// Core Render Logic
|
|
993
1055
|
function render() {
|
|
1056
|
+
if (!elRouteList || !elOverview) return;
|
|
1057
|
+
|
|
994
1058
|
// 1. Filter Data
|
|
995
|
-
const filtered = leaves.filter(leaf
|
|
1059
|
+
const filtered = leaves.filter(function(leaf) {
|
|
996
1060
|
const m = (leaf.method || 'GET').toUpperCase();
|
|
997
1061
|
const t = (leaf.cfg && leaf.cfg.tags) ? leaf.cfg.tags : [];
|
|
998
1062
|
const path = (leaf.path || '').toLowerCase();
|
|
999
1063
|
const summary = (leaf.cfg && leaf.cfg.summary || '').toLowerCase();
|
|
1000
1064
|
|
|
1001
1065
|
if (!filters.methods.has(m)) return false;
|
|
1002
|
-
if (filters.tags.size > 0 && !t.some(tag
|
|
1066
|
+
if (filters.tags.size > 0 && !t.some(function(tag) { return filters.tags.has(tag); })) return false;
|
|
1003
1067
|
if (filters.search && !path.includes(filters.search) && !summary.includes(filters.search)) return false;
|
|
1004
1068
|
|
|
1005
1069
|
return true;
|
|
@@ -1013,7 +1077,7 @@ var DOCS_JS = `
|
|
|
1013
1077
|
|
|
1014
1078
|
// 2. Group Data
|
|
1015
1079
|
const groups = {};
|
|
1016
|
-
filtered.forEach(leaf
|
|
1080
|
+
filtered.forEach(function(leaf) {
|
|
1017
1081
|
const gName = (leaf.cfg && leaf.cfg.docsGroup) ? leaf.cfg.docsGroup : 'UNGROUPED';
|
|
1018
1082
|
if (!groups[gName]) groups[gName] = [];
|
|
1019
1083
|
groups[gName].push(leaf);
|
|
@@ -1021,19 +1085,23 @@ var DOCS_JS = `
|
|
|
1021
1085
|
|
|
1022
1086
|
const sortedGroupNames = Object.keys(groups).sort(sortGroups);
|
|
1023
1087
|
|
|
1024
|
-
// 3. Render Overview Chips
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1088
|
+
// 3. Render Overview Chips
|
|
1089
|
+
elOverview.innerHTML = (
|
|
1090
|
+
'<span class="overview-label">JUMP TO:</span>' +
|
|
1091
|
+
sortedGroupNames.map(function(gName) {
|
|
1092
|
+
return (
|
|
1093
|
+
'<a href="#group-' + escapeAttr(gName) + '" ' +
|
|
1094
|
+
'class="group-chip" data-group="' + escapeAttr(gName) + '">' +
|
|
1095
|
+
escapeHtml(gName) +
|
|
1096
|
+
'</a>'
|
|
1097
|
+
);
|
|
1098
|
+
}).join('')
|
|
1099
|
+
);
|
|
1031
1100
|
|
|
1032
1101
|
// 4. Render Groups & Cards
|
|
1033
|
-
const html = sortedGroupNames.map(gName
|
|
1102
|
+
const html = sortedGroupNames.map(function(gName) {
|
|
1034
1103
|
const routes = groups[gName];
|
|
1035
|
-
|
|
1036
|
-
routes.sort((a, b) => {
|
|
1104
|
+
routes.sort(function(a, b) {
|
|
1037
1105
|
const pA = a.path || '';
|
|
1038
1106
|
const pB = b.path || '';
|
|
1039
1107
|
if (pA < pB) return -1;
|
|
@@ -1041,23 +1109,27 @@ var DOCS_JS = `
|
|
|
1041
1109
|
return (a.method || '').localeCompare(b.method || '');
|
|
1042
1110
|
});
|
|
1043
1111
|
|
|
1044
|
-
return
|
|
1045
|
-
<div class="api-group" id="group
|
|
1046
|
-
<div class="group-header">
|
|
1047
|
-
<h2 class="group-title"
|
|
1048
|
-
<div class="group-actions">
|
|
1049
|
-
<button
|
|
1050
|
-
<button
|
|
1051
|
-
</div>
|
|
1052
|
-
</div>
|
|
1053
|
-
<div class="cards-list">
|
|
1054
|
-
|
|
1055
|
-
</div>
|
|
1056
|
-
</div>
|
|
1057
|
-
|
|
1112
|
+
return (
|
|
1113
|
+
'<div class="api-group" id="group-' + escapeAttr(gName) + '">' +
|
|
1114
|
+
'<div class="group-header">' +
|
|
1115
|
+
'<h2 class="group-title">' + escapeHtml(gName) + '</h2>' +
|
|
1116
|
+
'<div class="group-actions">' +
|
|
1117
|
+
'<button type="button" data-group="' + escapeAttr(gName) + '" data-action="expand">Expand All</button>' +
|
|
1118
|
+
'<button type="button" data-group="' + escapeAttr(gName) + '" data-action="collapse">Collapse All</button>' +
|
|
1119
|
+
'</div>' +
|
|
1120
|
+
'</div>' +
|
|
1121
|
+
'<div class="cards-list">' +
|
|
1122
|
+
routes.map(renderCard).join('') +
|
|
1123
|
+
'</div>' +
|
|
1124
|
+
'</div>'
|
|
1125
|
+
);
|
|
1058
1126
|
}).join('');
|
|
1059
1127
|
|
|
1060
1128
|
elRouteList.innerHTML = html;
|
|
1129
|
+
|
|
1130
|
+
// Wire up interactions that depend on rendered DOM
|
|
1131
|
+
wireOverviewInteractions();
|
|
1132
|
+
wireCardInteractions();
|
|
1061
1133
|
}
|
|
1062
1134
|
|
|
1063
1135
|
// Card Renderer
|
|
@@ -1069,35 +1141,33 @@ var DOCS_JS = `
|
|
|
1069
1141
|
const description = cfg.description || '';
|
|
1070
1142
|
const tags = cfg.tags || [];
|
|
1071
1143
|
|
|
1072
|
-
|
|
1073
|
-
const tagBadges = tags.map(t => {
|
|
1144
|
+
const tagBadges = tags.map(function(t) {
|
|
1074
1145
|
const cClass = getTagColorClass(t);
|
|
1075
|
-
return
|
|
1146
|
+
return '<span class="status-badge ' + cClass + '">' + escapeHtml(t) + '</span>';
|
|
1076
1147
|
}).join('');
|
|
1077
1148
|
|
|
1078
|
-
const headerHtml =
|
|
1079
|
-
<div class="card-header">
|
|
1080
|
-
<span class="method-badge m
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
<span class="
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
</div>
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
</div></div>\`;
|
|
1149
|
+
const headerHtml =
|
|
1150
|
+
'<div class="card-header">' +
|
|
1151
|
+
'<span class="method-badge m-' + escapeAttr(method) + '">' + escapeHtml(method) + '</span>' +
|
|
1152
|
+
'<div class="path-container" data-path="' + escapeAttr(path) + '" title="Click to copy path">' +
|
|
1153
|
+
'<span class="path-text">' + escapeHtml(path) + '</span>' +
|
|
1154
|
+
'<span class="copy-icon">\u{1F4CB}</span>' +
|
|
1155
|
+
'</div>' +
|
|
1156
|
+
'<div class="tags-container">' +
|
|
1157
|
+
tagBadges +
|
|
1158
|
+
'</div>' +
|
|
1159
|
+
'<div class="header-spacer"></div>' +
|
|
1160
|
+
'<div class="expand-icon">\u25BC</div>' +
|
|
1161
|
+
'</div>';
|
|
1162
|
+
|
|
1163
|
+
let contentHtml =
|
|
1164
|
+
'<div class="section-block"><div class="summary-text">' +
|
|
1165
|
+
'<strong>' + escapeHtml(summary) + '</strong>' +
|
|
1166
|
+
(description
|
|
1167
|
+
? '<br><span class="description-text">' + escapeHtml(description) + '</span>'
|
|
1168
|
+
: ''
|
|
1169
|
+
) +
|
|
1170
|
+
'</div></div>';
|
|
1101
1171
|
|
|
1102
1172
|
if (cfg.paramsSchema || cfg.querySchema) {
|
|
1103
1173
|
contentHtml += '<div class="section-block"><div class="section-title">Parameters</div>';
|
|
@@ -1107,96 +1177,331 @@ var DOCS_JS = `
|
|
|
1107
1177
|
}
|
|
1108
1178
|
|
|
1109
1179
|
if (cfg.bodySchema) {
|
|
1110
|
-
contentHtml +=
|
|
1111
|
-
<div class="section-block">
|
|
1112
|
-
<div class="section-title">Request Body
|
|
1113
|
-
|
|
1114
|
-
</div>
|
|
1115
|
-
\`;
|
|
1180
|
+
contentHtml +=
|
|
1181
|
+
'<div class="section-block">' +
|
|
1182
|
+
'<div class="section-title">Request Body ' + (cfg.hasBody ? '' : '(Optional)') + '</div>' +
|
|
1183
|
+
renderSchemaTable(cfg.bodySchema) +
|
|
1184
|
+
'</div>';
|
|
1116
1185
|
}
|
|
1117
1186
|
|
|
1118
1187
|
if (cfg.outputSchema) {
|
|
1119
|
-
contentHtml +=
|
|
1120
|
-
<div class="section-block">
|
|
1121
|
-
<div class="section-title">Response Schema</div>
|
|
1122
|
-
|
|
1123
|
-
</div>
|
|
1124
|
-
\`;
|
|
1188
|
+
contentHtml +=
|
|
1189
|
+
'<div class="section-block">' +
|
|
1190
|
+
'<div class="section-title">Response Schema</div>' +
|
|
1191
|
+
renderSchemaTable(cfg.outputSchema) +
|
|
1192
|
+
'</div>';
|
|
1125
1193
|
}
|
|
1126
1194
|
|
|
1127
|
-
return
|
|
1128
|
-
<article class="endpoint-card" data-expanded="false"
|
|
1129
|
-
|
|
1130
|
-
<div class="card-body"
|
|
1131
|
-
|
|
1132
|
-
</div>
|
|
1133
|
-
</article>
|
|
1134
|
-
|
|
1195
|
+
return (
|
|
1196
|
+
'<article class="endpoint-card" data-expanded="false">' +
|
|
1197
|
+
headerHtml +
|
|
1198
|
+
'<div class="card-body">' +
|
|
1199
|
+
contentHtml +
|
|
1200
|
+
'</div>' +
|
|
1201
|
+
'</article>'
|
|
1202
|
+
);
|
|
1135
1203
|
}
|
|
1136
1204
|
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1205
|
+
|
|
1206
|
+
function renderSchemaTable(node, title) {
|
|
1207
|
+
let rows = '';
|
|
1208
|
+
|
|
1209
|
+
if (node.kind === 'object' && node.properties) {
|
|
1210
|
+
rows = renderObjectChildren(node, 0);
|
|
1211
|
+
} else {
|
|
1212
|
+
rows = renderNodeRow('(root)', node, 0);
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
return (
|
|
1216
|
+
(title
|
|
1217
|
+
? '<div class="schema-subtitle">' + escapeHtml(title) + '</div>'
|
|
1218
|
+
: '') +
|
|
1219
|
+
'<table class="schema-table">' +
|
|
1220
|
+
rows +
|
|
1221
|
+
'</table>'
|
|
1222
|
+
);
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
/**
|
|
1226
|
+
* Render all children of an object node (at a given depth).
|
|
1227
|
+
*/
|
|
1228
|
+
function renderObjectChildren(node, depth) {
|
|
1229
|
+
if (!node.properties) return '';
|
|
1230
|
+
|
|
1231
|
+
let rows = '';
|
|
1232
|
+
Object.keys(node.properties).forEach(function (key) {
|
|
1233
|
+
rows += renderNodeRow(key, node.properties[key], depth);
|
|
1234
|
+
});
|
|
1235
|
+
return rows;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
/**
|
|
1239
|
+
* Render a single row for a property / node, then recursively render nested structure.
|
|
1240
|
+
*/
|
|
1241
|
+
function renderNodeRow(name, node, depth) {
|
|
1242
|
+
const reqClass = node.optional ? 'req-false' : 'req-true';
|
|
1243
|
+
const reqText = node.optional ? 'OPT' : 'REQ';
|
|
1244
|
+
|
|
1245
|
+
const indentPx = depth * 16;
|
|
1246
|
+
const nameCell =
|
|
1247
|
+
'<span class="schema-indent" style="padding-left:' +
|
|
1248
|
+
indentPx +
|
|
1249
|
+
'px;"></span>' +
|
|
1250
|
+
(depth > 0 ? '<span class="schema-branch">\u2514\u2500 </span>' : '') +
|
|
1251
|
+
escapeHtml(name);
|
|
1252
|
+
|
|
1253
|
+
const typeLabel = getTypeLabel(node);
|
|
1254
|
+
const descriptionHtml = renderNodeDescription(node);
|
|
1255
|
+
|
|
1256
|
+
let rows =
|
|
1257
|
+
'<tr>' +
|
|
1258
|
+
'<td class="col-name">' + nameCell + '</td>' +
|
|
1259
|
+
'<td class="col-type">' + escapeHtml(typeLabel) + '</td>' +
|
|
1260
|
+
'<td><span class="req-badge ' + reqClass + '">' + reqText + '</span></td>' +
|
|
1261
|
+
'<td>' + descriptionHtml + '</td>' +
|
|
1262
|
+
'</tr>';
|
|
1263
|
+
|
|
1264
|
+
// 1) Object properties
|
|
1265
|
+
if (node.kind === 'object' && node.properties) {
|
|
1266
|
+
rows += renderObjectChildren(node, depth + 1);
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
// 2) Array element type
|
|
1270
|
+
if (node.kind === 'array' && node.element) {
|
|
1271
|
+
const element = node.element;
|
|
1272
|
+
if (isComplexNode(element)) {
|
|
1273
|
+
rows += renderNodeRow('[items]', element, depth + 1);
|
|
1155
1274
|
}
|
|
1156
|
-
return \`
|
|
1157
|
-
\${title ? '<div style="font-size:11px; font-weight:600; margin-bottom:4px; color:#64748b">' + title + '</div>' : ''}
|
|
1158
|
-
<table class="schema-table">\${rows}</table>
|
|
1159
|
-
\`;
|
|
1160
1275
|
}
|
|
1161
1276
|
|
|
1277
|
+
// 3) Union variants
|
|
1278
|
+
if (node.kind === 'union' && Array.isArray(node.union)) {
|
|
1279
|
+
node.union.forEach(function (variant, index) {
|
|
1280
|
+
rows += renderNodeRow('option ' + (index + 1), variant, depth + 1);
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
return rows;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
/**
|
|
1288
|
+
* Decide if a node is \u201Ccomplex\u201D enough to warrant a nested expansion row.
|
|
1289
|
+
*/
|
|
1290
|
+
function isComplexNode(node) {
|
|
1291
|
+
return (
|
|
1292
|
+
node.kind === 'object' ||
|
|
1293
|
+
node.kind === 'array' ||
|
|
1294
|
+
node.kind === 'union' ||
|
|
1295
|
+
node.kind === 'record' ||
|
|
1296
|
+
node.kind === 'tuple'
|
|
1297
|
+
);
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
/**
|
|
1301
|
+
* Helper: simple vs complex node, for compact type labels.
|
|
1302
|
+
*/
|
|
1303
|
+
function isSimpleNode(node) {
|
|
1304
|
+
return !isComplexNode(node);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
/**
|
|
1308
|
+
* Type label shown in the "Type" column.
|
|
1309
|
+
*/
|
|
1310
|
+
function getTypeLabel(node) {
|
|
1311
|
+
let base;
|
|
1312
|
+
|
|
1313
|
+
switch (node.kind) {
|
|
1314
|
+
case 'object':
|
|
1315
|
+
base = 'object';
|
|
1316
|
+
break;
|
|
1317
|
+
|
|
1318
|
+
case 'array':
|
|
1319
|
+
if (!node.element) {
|
|
1320
|
+
base = 'array';
|
|
1321
|
+
} else if (isSimpleNode(node.element)) {
|
|
1322
|
+
base = 'array<' + getTypeLabel(node.element) + '>';
|
|
1323
|
+
} else {
|
|
1324
|
+
base = 'array<object>';
|
|
1325
|
+
}
|
|
1326
|
+
break;
|
|
1327
|
+
|
|
1328
|
+
case 'union':
|
|
1329
|
+
if (node.union && node.union.length) {
|
|
1330
|
+
const parts = node.union.map(function (u) {
|
|
1331
|
+
return isSimpleNode(u) ? getTypeLabel(u) : 'object';
|
|
1332
|
+
});
|
|
1333
|
+
base = parts.join(' | ');
|
|
1334
|
+
} else {
|
|
1335
|
+
base = 'union';
|
|
1336
|
+
}
|
|
1337
|
+
break;
|
|
1338
|
+
|
|
1339
|
+
case 'literal':
|
|
1340
|
+
base = 'literal';
|
|
1341
|
+
break;
|
|
1342
|
+
|
|
1343
|
+
case 'enum':
|
|
1344
|
+
base = 'enum';
|
|
1345
|
+
break;
|
|
1346
|
+
|
|
1347
|
+
case 'record':
|
|
1348
|
+
base = 'record';
|
|
1349
|
+
break;
|
|
1350
|
+
|
|
1351
|
+
case 'tuple':
|
|
1352
|
+
base = 'tuple';
|
|
1353
|
+
break;
|
|
1354
|
+
|
|
1355
|
+
default:
|
|
1356
|
+
base = node.kind || 'unknown';
|
|
1357
|
+
break;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
if (node.nullable) {
|
|
1361
|
+
base = base + ' | null';
|
|
1362
|
+
}
|
|
1363
|
+
return base;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
/**
|
|
1367
|
+
* Description cell content:
|
|
1368
|
+
* - main description text
|
|
1369
|
+
* - for enum: allowed values
|
|
1370
|
+
* - for literal: literal value
|
|
1371
|
+
*/
|
|
1372
|
+
function renderNodeDescription(node) {
|
|
1373
|
+
const parts = [];
|
|
1374
|
+
|
|
1375
|
+
if (node.description) {
|
|
1376
|
+
parts.push(escapeHtml(node.description));
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
if (node.kind === 'enum' && node.enumValues && node.enumValues.length) {
|
|
1380
|
+
const values = node.enumValues
|
|
1381
|
+
.map(function (v) {
|
|
1382
|
+
return '<code>' + escapeHtml(String(v)) + '</code>';
|
|
1383
|
+
})
|
|
1384
|
+
.join(' | ');
|
|
1385
|
+
parts.push('<div class="schema-meta">Allowed: ' + values + '</div>');
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
if (node.kind === 'literal' && typeof node.literal !== 'undefined') {
|
|
1389
|
+
const valueStr = escapeHtml(JSON.stringify(node.literal));
|
|
1390
|
+
parts.push(
|
|
1391
|
+
'<div class="schema-meta">Literal: <code>' + valueStr + '</code></div>'
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
return parts.join('<br>');
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
/**
|
|
1399
|
+
* Escape HTML helper.
|
|
1400
|
+
*/
|
|
1401
|
+
function escapeHtml(str) {
|
|
1402
|
+
return String(str)
|
|
1403
|
+
.replace(/&/g, '&')
|
|
1404
|
+
.replace(/</g, '<')
|
|
1405
|
+
.replace(/>/g, '>')
|
|
1406
|
+
.replace(/"/g, '"')
|
|
1407
|
+
.replace(/'/g, ''');
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1162
1410
|
function getTypeLabel(node) {
|
|
1163
1411
|
if (!node) return 'any';
|
|
1164
|
-
if (node.kind === 'array') return
|
|
1165
|
-
if (node.enumValues) return
|
|
1412
|
+
if (node.kind === 'array') return getTypeLabel(node.element) + '[]';
|
|
1413
|
+
if (node.enumValues) return 'enum(' + node.enumValues.join('|') + ')';
|
|
1166
1414
|
return node.kind || 'any';
|
|
1167
1415
|
}
|
|
1168
1416
|
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1417
|
+
// Interactions
|
|
1418
|
+
|
|
1419
|
+
function wireOverviewInteractions() {
|
|
1420
|
+
if (!elOverview || !elRouteList) return;
|
|
1421
|
+
|
|
1422
|
+
// group chips
|
|
1423
|
+
elOverview.querySelectorAll('.group-chip').forEach(function(chip) {
|
|
1424
|
+
chip.addEventListener('click', function(e) {
|
|
1425
|
+
e.preventDefault();
|
|
1426
|
+
const gName = chip.getAttribute('data-group');
|
|
1427
|
+
if (gName) scrollToGroup(gName);
|
|
1428
|
+
});
|
|
1429
|
+
});
|
|
1430
|
+
|
|
1431
|
+
// expand/collapse buttons
|
|
1432
|
+
elRouteList.querySelectorAll('.group-actions button').forEach(function(btn) {
|
|
1433
|
+
btn.addEventListener('click', function() {
|
|
1434
|
+
const gName = btn.getAttribute('data-group');
|
|
1435
|
+
const action = btn.getAttribute('data-action');
|
|
1436
|
+
if (!gName || !action) return;
|
|
1437
|
+
toggleGroup(gName, action === 'expand');
|
|
1438
|
+
});
|
|
1439
|
+
});
|
|
1172
1440
|
}
|
|
1173
1441
|
|
|
1174
|
-
|
|
1175
|
-
if (
|
|
1442
|
+
function wireCardInteractions() {
|
|
1443
|
+
if (!elRouteList) return;
|
|
1444
|
+
|
|
1445
|
+
// card expand/collapse
|
|
1446
|
+
elRouteList.querySelectorAll('.endpoint-card').forEach(function(card) {
|
|
1447
|
+
card.addEventListener('click', function(e) {
|
|
1448
|
+
var target = e.target;
|
|
1449
|
+
if (target && target.closest && target.closest('.path-container')) {
|
|
1450
|
+
// handled separately for copy
|
|
1451
|
+
return;
|
|
1452
|
+
}
|
|
1453
|
+
toggleCard(card);
|
|
1454
|
+
});
|
|
1455
|
+
});
|
|
1456
|
+
|
|
1457
|
+
// copy path
|
|
1458
|
+
elRouteList.querySelectorAll('.path-container').forEach(function(pc) {
|
|
1459
|
+
pc.addEventListener('click', function(e) {
|
|
1460
|
+
e.stopPropagation();
|
|
1461
|
+
const text = pc.getAttribute('data-path') || '';
|
|
1462
|
+
copyText(text);
|
|
1463
|
+
});
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
function toggleCard(card) {
|
|
1176
1468
|
const isExpanded = card.getAttribute('data-expanded') === 'true';
|
|
1177
|
-
card.setAttribute('data-expanded', !isExpanded);
|
|
1178
|
-
}
|
|
1469
|
+
card.setAttribute('data-expanded', String(!isExpanded));
|
|
1470
|
+
}
|
|
1179
1471
|
|
|
1180
|
-
|
|
1472
|
+
function toggleGroup(gName, expand) {
|
|
1181
1473
|
const group = document.getElementById('group-' + gName);
|
|
1182
1474
|
if (!group) return;
|
|
1183
|
-
group.querySelectorAll('.endpoint-card').forEach(c
|
|
1184
|
-
|
|
1475
|
+
group.querySelectorAll('.endpoint-card').forEach(function(c) {
|
|
1476
|
+
c.setAttribute('data-expanded', String(!!expand));
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1185
1479
|
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
navigator.clipboard.writeText(text)
|
|
1189
|
-
|
|
1190
|
-
|
|
1480
|
+
function copyText(text) {
|
|
1481
|
+
if (!navigator.clipboard || !text) return;
|
|
1482
|
+
navigator.clipboard.writeText(text).catch(function() {
|
|
1483
|
+
// ignore
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1191
1486
|
|
|
1192
|
-
|
|
1193
|
-
e.preventDefault();
|
|
1487
|
+
function scrollToGroup(gName) {
|
|
1194
1488
|
const el = document.getElementById('group-' + gName);
|
|
1195
|
-
if(el) window.scrollTo({ top: el.offsetTop - 220, behavior: 'smooth' });
|
|
1489
|
+
if (el) window.scrollTo({ top: el.offsetTop - 220, behavior: 'smooth' });
|
|
1196
1490
|
}
|
|
1197
1491
|
|
|
1198
|
-
|
|
1199
|
-
|
|
1492
|
+
// Escaping helpers
|
|
1493
|
+
function escapeHtml(str) {
|
|
1494
|
+
if (!str && str !== 0) return '';
|
|
1495
|
+
return String(str)
|
|
1496
|
+
.replace(/&/g, '&')
|
|
1497
|
+
.replace(/</g, '<')
|
|
1498
|
+
.replace(/>/g, '>')
|
|
1499
|
+
.replace(/"/g, '"')
|
|
1500
|
+
.replace(/'/g, ''');
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
function escapeAttr(str) {
|
|
1504
|
+
return escapeHtml(str);
|
|
1200
1505
|
}
|
|
1201
1506
|
|
|
1202
1507
|
init();
|
|
@@ -1226,12 +1531,9 @@ function renderLeafDocsHTML(leaves, options = {}) {
|
|
|
1226
1531
|
|
|
1227
1532
|
<div class="controls-container">
|
|
1228
1533
|
<div class="filters-row">
|
|
1229
|
-
|
|
1230
1534
|
<div class="left-column">
|
|
1231
|
-
|
|
1232
1535
|
<div class="filter-group method-filters-container">
|
|
1233
1536
|
<div class="filter-label">Search</div>
|
|
1234
|
-
|
|
1235
1537
|
<div class="search-box">
|
|
1236
1538
|
<span class="search-icon">\u{1F50D}</span>
|
|
1237
1539
|
<input type="text" id="searchInput" class="search-input" placeholder="Filter endpoints...">
|