@kirrosh/zond 0.19.0 → 0.21.0
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/package.json +2 -2
- package/src/cli/commands/catalog.ts +62 -0
- package/src/cli/commands/generate.ts +18 -1
- package/src/cli/commands/sync.ts +28 -0
- package/src/cli/index.ts +18 -0
- package/src/core/generator/catalog-builder.ts +179 -0
- package/src/core/generator/index.ts +2 -0
- package/src/db/queries.ts +6 -4
- package/src/web/data/collection-state.ts +2 -0
- package/src/web/routes/api.ts +80 -0
- package/src/web/routes/dashboard.ts +37 -0
- package/src/web/static/style.css +321 -0
- package/src/web/views/endpoints-tab.ts +11 -7
- package/src/web/views/explorer-tab.ts +402 -0
- package/src/web/views/results.ts +6 -5
- package/src/web/views/suites-tab.ts +32 -4
package/src/web/static/style.css
CHANGED
|
@@ -816,6 +816,325 @@ h3 { font-size: 1.05rem; margin: 1rem 0 0.25rem; }
|
|
|
816
816
|
.history-row:hover { background: var(--bg-hover); }
|
|
817
817
|
.pagination { display: flex; gap: 0.5rem; margin: 1.5rem 0; align-items: center; }
|
|
818
818
|
|
|
819
|
+
/* ── Step method+path+status labels ── */
|
|
820
|
+
.step-path { font-family: var(--font-mono); font-size: 0.8rem; }
|
|
821
|
+
.step-status-code {
|
|
822
|
+
display: inline-block;
|
|
823
|
+
font-family: var(--font-mono);
|
|
824
|
+
font-size: 0.65rem;
|
|
825
|
+
font-weight: 600;
|
|
826
|
+
padding: 0.1rem 0.35rem;
|
|
827
|
+
border-radius: 3px;
|
|
828
|
+
margin-left: 0.35rem;
|
|
829
|
+
vertical-align: middle;
|
|
830
|
+
}
|
|
831
|
+
.step-status-code.status-ok { background: var(--pass-dim); color: var(--pass); }
|
|
832
|
+
.step-status-code.status-error { background: var(--fail-dim); color: var(--fail); }
|
|
833
|
+
.step-name-dim { color: var(--text-dim); font-size: 0.7rem; margin-left: 0.5rem; }
|
|
834
|
+
|
|
835
|
+
/* ── Suite link (endpoints → suites navigation) ── */
|
|
836
|
+
.suite-link {
|
|
837
|
+
color: var(--accent);
|
|
838
|
+
text-decoration: none;
|
|
839
|
+
cursor: pointer;
|
|
840
|
+
}
|
|
841
|
+
.suite-link:hover { text-decoration: underline; }
|
|
842
|
+
.suite-highlight {
|
|
843
|
+
animation: suite-flash 2s ease-out;
|
|
844
|
+
}
|
|
845
|
+
@keyframes suite-flash {
|
|
846
|
+
0% { background: rgba(61, 139, 253, 0.25); }
|
|
847
|
+
100% { background: transparent; }
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/* ═══════════════════════════════════════════════════
|
|
851
|
+
Explorer Tab
|
|
852
|
+
═══════════════════════════════════════════════════ */
|
|
853
|
+
|
|
854
|
+
.explorer-base-url {
|
|
855
|
+
display: flex;
|
|
856
|
+
align-items: center;
|
|
857
|
+
gap: 0.5rem;
|
|
858
|
+
padding: 0.75rem 1rem;
|
|
859
|
+
background: var(--bg-secondary);
|
|
860
|
+
border: 1px solid var(--border);
|
|
861
|
+
border-radius: var(--radius);
|
|
862
|
+
margin-bottom: 1rem;
|
|
863
|
+
}
|
|
864
|
+
.explorer-base-url .explorer-label {
|
|
865
|
+
font-size: 0.8rem;
|
|
866
|
+
font-weight: 600;
|
|
867
|
+
color: var(--text-dim);
|
|
868
|
+
white-space: nowrap;
|
|
869
|
+
}
|
|
870
|
+
.explorer-input {
|
|
871
|
+
background: var(--bg-inset);
|
|
872
|
+
border: 1px solid var(--border);
|
|
873
|
+
border-radius: var(--radius-sm);
|
|
874
|
+
color: var(--text);
|
|
875
|
+
font-family: var(--font-mono);
|
|
876
|
+
font-size: 0.85rem;
|
|
877
|
+
padding: 0.4rem 0.6rem;
|
|
878
|
+
flex: 1;
|
|
879
|
+
min-width: 0;
|
|
880
|
+
}
|
|
881
|
+
.explorer-input:focus {
|
|
882
|
+
outline: none;
|
|
883
|
+
border-color: var(--accent);
|
|
884
|
+
}
|
|
885
|
+
.explorer-input-sm { flex: unset; width: auto; }
|
|
886
|
+
|
|
887
|
+
/* Groups */
|
|
888
|
+
.explorer-list { display: flex; flex-direction: column; gap: 0.5rem; }
|
|
889
|
+
.explorer-group {
|
|
890
|
+
border: 1px solid var(--border);
|
|
891
|
+
border-radius: var(--radius);
|
|
892
|
+
overflow: hidden;
|
|
893
|
+
}
|
|
894
|
+
.explorer-group-title {
|
|
895
|
+
font-size: 0.85rem;
|
|
896
|
+
font-weight: 600;
|
|
897
|
+
padding: 0.6rem 1rem;
|
|
898
|
+
background: var(--bg-secondary);
|
|
899
|
+
cursor: pointer;
|
|
900
|
+
display: flex;
|
|
901
|
+
align-items: center;
|
|
902
|
+
gap: 0.5rem;
|
|
903
|
+
}
|
|
904
|
+
.explorer-group-title .tab-count {
|
|
905
|
+
font-size: 0.75rem;
|
|
906
|
+
background: var(--bg-hover);
|
|
907
|
+
padding: 0.1rem 0.5rem;
|
|
908
|
+
border-radius: var(--radius-pill);
|
|
909
|
+
color: var(--text-dim);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/* Endpoint row */
|
|
913
|
+
.explorer-endpoint {
|
|
914
|
+
display: flex;
|
|
915
|
+
align-items: center;
|
|
916
|
+
gap: 0.5rem;
|
|
917
|
+
padding: 0.5rem 1rem;
|
|
918
|
+
cursor: pointer;
|
|
919
|
+
border-top: 1px solid var(--border-subtle);
|
|
920
|
+
transition: background 0.15s;
|
|
921
|
+
}
|
|
922
|
+
.explorer-endpoint:hover { background: var(--bg-hover); }
|
|
923
|
+
.explorer-endpoint-path {
|
|
924
|
+
font-family: var(--font-mono);
|
|
925
|
+
font-size: 0.85rem;
|
|
926
|
+
font-weight: 500;
|
|
927
|
+
}
|
|
928
|
+
.explorer-endpoint-summary {
|
|
929
|
+
color: var(--text-dim);
|
|
930
|
+
font-size: 0.8rem;
|
|
931
|
+
margin-left: auto;
|
|
932
|
+
white-space: nowrap;
|
|
933
|
+
overflow: hidden;
|
|
934
|
+
text-overflow: ellipsis;
|
|
935
|
+
}
|
|
936
|
+
.explorer-auth-hint {
|
|
937
|
+
font-size: 0.65rem;
|
|
938
|
+
font-weight: 600;
|
|
939
|
+
padding: 0.1rem 0.35rem;
|
|
940
|
+
border-radius: var(--radius-sm);
|
|
941
|
+
background: var(--warn-dim);
|
|
942
|
+
color: var(--warn);
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
/* Detail / form */
|
|
946
|
+
.explorer-detail {
|
|
947
|
+
padding: 1rem;
|
|
948
|
+
background: var(--bg-raised);
|
|
949
|
+
border-top: 1px solid var(--border);
|
|
950
|
+
}
|
|
951
|
+
.explorer-section {
|
|
952
|
+
margin-bottom: 1rem;
|
|
953
|
+
}
|
|
954
|
+
.explorer-section-title {
|
|
955
|
+
font-size: 0.8rem;
|
|
956
|
+
font-weight: 600;
|
|
957
|
+
color: var(--text-dim);
|
|
958
|
+
margin-bottom: 0.5rem;
|
|
959
|
+
display: flex;
|
|
960
|
+
align-items: center;
|
|
961
|
+
gap: 0.5rem;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
/* Parameters */
|
|
965
|
+
.explorer-param-row {
|
|
966
|
+
display: grid;
|
|
967
|
+
grid-template-columns: 140px 50px 90px 1fr;
|
|
968
|
+
gap: 0.5rem;
|
|
969
|
+
align-items: center;
|
|
970
|
+
margin-bottom: 0.35rem;
|
|
971
|
+
font-size: 0.85rem;
|
|
972
|
+
}
|
|
973
|
+
.explorer-param-name {
|
|
974
|
+
font-family: var(--font-mono);
|
|
975
|
+
font-weight: 500;
|
|
976
|
+
font-size: 0.8rem;
|
|
977
|
+
}
|
|
978
|
+
.explorer-param-location {
|
|
979
|
+
font-size: 0.7rem;
|
|
980
|
+
color: var(--text-muted);
|
|
981
|
+
background: var(--bg-inset);
|
|
982
|
+
padding: 0.1rem 0.3rem;
|
|
983
|
+
border-radius: var(--radius-sm);
|
|
984
|
+
text-align: center;
|
|
985
|
+
}
|
|
986
|
+
.explorer-param-type {
|
|
987
|
+
font-size: 0.75rem;
|
|
988
|
+
color: var(--text-dim);
|
|
989
|
+
font-family: var(--font-mono);
|
|
990
|
+
}
|
|
991
|
+
.explorer-required {
|
|
992
|
+
color: var(--fail);
|
|
993
|
+
font-weight: 700;
|
|
994
|
+
margin-left: 0.15rem;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
/* Body editor */
|
|
998
|
+
.explorer-body-editor {
|
|
999
|
+
width: 100%;
|
|
1000
|
+
min-height: 120px;
|
|
1001
|
+
background: var(--bg-inset);
|
|
1002
|
+
border: 1px solid var(--border);
|
|
1003
|
+
border-radius: var(--radius-sm);
|
|
1004
|
+
color: var(--text);
|
|
1005
|
+
font-family: var(--font-mono);
|
|
1006
|
+
font-size: 0.85rem;
|
|
1007
|
+
padding: 0.5rem;
|
|
1008
|
+
resize: vertical;
|
|
1009
|
+
tab-size: 2;
|
|
1010
|
+
}
|
|
1011
|
+
.explorer-body-editor:focus { outline: none; border-color: var(--accent); }
|
|
1012
|
+
.explorer-content-type {
|
|
1013
|
+
font-size: 0.75rem;
|
|
1014
|
+
padding: 0.15rem 0.35rem;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/* Custom headers */
|
|
1018
|
+
.explorer-headers-list {
|
|
1019
|
+
display: flex;
|
|
1020
|
+
flex-direction: column;
|
|
1021
|
+
gap: 0.35rem;
|
|
1022
|
+
}
|
|
1023
|
+
.explorer-header-pair {
|
|
1024
|
+
display: flex;
|
|
1025
|
+
gap: 0.35rem;
|
|
1026
|
+
align-items: center;
|
|
1027
|
+
}
|
|
1028
|
+
.explorer-header-pair input { flex: 1; }
|
|
1029
|
+
.explorer-remove-btn {
|
|
1030
|
+
background: none;
|
|
1031
|
+
border: 1px solid var(--border);
|
|
1032
|
+
border-radius: var(--radius-sm);
|
|
1033
|
+
color: var(--text-dim);
|
|
1034
|
+
cursor: pointer;
|
|
1035
|
+
font-size: 0.75rem;
|
|
1036
|
+
padding: 0.25rem 0.5rem;
|
|
1037
|
+
line-height: 1;
|
|
1038
|
+
}
|
|
1039
|
+
.explorer-remove-btn:hover { color: var(--fail); border-color: var(--fail); }
|
|
1040
|
+
.explorer-add-header-btn {
|
|
1041
|
+
background: none;
|
|
1042
|
+
border: none;
|
|
1043
|
+
color: var(--accent);
|
|
1044
|
+
cursor: pointer;
|
|
1045
|
+
font-size: 0.8rem;
|
|
1046
|
+
padding: 0.25rem 0;
|
|
1047
|
+
margin-top: 0.25rem;
|
|
1048
|
+
}
|
|
1049
|
+
.explorer-add-header-btn:hover { text-decoration: underline; }
|
|
1050
|
+
|
|
1051
|
+
/* Actions */
|
|
1052
|
+
.explorer-actions {
|
|
1053
|
+
display: flex;
|
|
1054
|
+
align-items: center;
|
|
1055
|
+
gap: 0.75rem;
|
|
1056
|
+
margin-top: 0.5rem;
|
|
1057
|
+
}
|
|
1058
|
+
.explorer-send-btn {
|
|
1059
|
+
background: var(--accent);
|
|
1060
|
+
color: #fff;
|
|
1061
|
+
border: none;
|
|
1062
|
+
border-radius: var(--radius);
|
|
1063
|
+
padding: 0.5rem 1.5rem;
|
|
1064
|
+
font-weight: 600;
|
|
1065
|
+
font-size: 0.85rem;
|
|
1066
|
+
cursor: pointer;
|
|
1067
|
+
transition: background 0.15s;
|
|
1068
|
+
}
|
|
1069
|
+
.explorer-send-btn:hover { background: var(--accent-hover); }
|
|
1070
|
+
.explorer-send-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
1071
|
+
.explorer-spinner { color: var(--text-dim); font-size: 0.85rem; }
|
|
1072
|
+
|
|
1073
|
+
/* Response */
|
|
1074
|
+
.explorer-response-container { margin-top: 1rem; }
|
|
1075
|
+
.explorer-response {
|
|
1076
|
+
border: 1px solid var(--border);
|
|
1077
|
+
border-radius: var(--radius);
|
|
1078
|
+
overflow: hidden;
|
|
1079
|
+
}
|
|
1080
|
+
.explorer-response-error { border-color: var(--fail); }
|
|
1081
|
+
.response-meta {
|
|
1082
|
+
display: flex;
|
|
1083
|
+
align-items: center;
|
|
1084
|
+
gap: 1rem;
|
|
1085
|
+
padding: 0.5rem 0.75rem;
|
|
1086
|
+
background: var(--bg-secondary);
|
|
1087
|
+
border-bottom: 1px solid var(--border);
|
|
1088
|
+
font-size: 0.85rem;
|
|
1089
|
+
}
|
|
1090
|
+
.response-status {
|
|
1091
|
+
font-weight: 700;
|
|
1092
|
+
font-family: var(--font-mono);
|
|
1093
|
+
}
|
|
1094
|
+
.response-status.status-2xx { color: var(--pass); }
|
|
1095
|
+
.response-status.status-3xx { color: var(--info); }
|
|
1096
|
+
.response-status.status-4xx { color: var(--warn); }
|
|
1097
|
+
.response-status.status-5xx { color: var(--fail); }
|
|
1098
|
+
.response-time { color: var(--text-dim); font-size: 0.8rem; }
|
|
1099
|
+
.response-size { color: var(--text-muted); font-size: 0.8rem; }
|
|
1100
|
+
.response-headers { padding: 0.5rem 0.75rem; }
|
|
1101
|
+
.response-headers summary {
|
|
1102
|
+
font-size: 0.8rem;
|
|
1103
|
+
color: var(--text-dim);
|
|
1104
|
+
cursor: pointer;
|
|
1105
|
+
}
|
|
1106
|
+
.response-headers-pre {
|
|
1107
|
+
font-size: 0.8rem;
|
|
1108
|
+
margin-top: 0.25rem;
|
|
1109
|
+
white-space: pre-wrap;
|
|
1110
|
+
word-break: break-all;
|
|
1111
|
+
}
|
|
1112
|
+
.response-body {
|
|
1113
|
+
padding: 0.75rem;
|
|
1114
|
+
background: var(--bg-inset);
|
|
1115
|
+
}
|
|
1116
|
+
.response-body pre {
|
|
1117
|
+
font-family: var(--font-mono);
|
|
1118
|
+
font-size: 0.8rem;
|
|
1119
|
+
white-space: pre-wrap;
|
|
1120
|
+
word-break: break-all;
|
|
1121
|
+
max-height: 500px;
|
|
1122
|
+
overflow-y: auto;
|
|
1123
|
+
margin: 0;
|
|
1124
|
+
}
|
|
1125
|
+
.response-error-msg {
|
|
1126
|
+
padding: 0.75rem;
|
|
1127
|
+
color: var(--fail);
|
|
1128
|
+
font-size: 0.85rem;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
/* JSON syntax highlighting */
|
|
1132
|
+
.json-key { color: var(--accent); }
|
|
1133
|
+
.json-string { color: var(--pass); }
|
|
1134
|
+
.json-number { color: var(--warn); }
|
|
1135
|
+
.json-boolean { color: var(--method-patch); }
|
|
1136
|
+
.json-null { color: var(--text-dim); font-style: italic; }
|
|
1137
|
+
|
|
819
1138
|
/* ── Responsive ── */
|
|
820
1139
|
@media (max-width: 768px) {
|
|
821
1140
|
.health-strip { grid-template-columns: 1fr; gap: 1rem; }
|
|
@@ -824,4 +1143,6 @@ h3 { font-size: 1.05rem; margin: 1rem 0 0.25rem; }
|
|
|
824
1143
|
.run-row { grid-template-columns: 48px 1fr 60px; }
|
|
825
1144
|
.run-time, .run-duration { display: none; }
|
|
826
1145
|
.runs-header { display: none; }
|
|
1146
|
+
.explorer-param-row { grid-template-columns: 1fr; }
|
|
1147
|
+
.explorer-endpoint-summary { display: none; }
|
|
827
1148
|
}
|
|
@@ -93,7 +93,7 @@ function renderWarningBadges(warnings: string[]): string {
|
|
|
93
93
|
if (w === "deprecated") return '<span class="warning-badge warning-deprecated">DEPRECATED</span>';
|
|
94
94
|
if (w === "no_response_schema") return '<span class="warning-badge warning-schema">NO SCHEMA</span>';
|
|
95
95
|
if (w === "no_responses_defined") return '<span class="warning-badge warning-schema">NO RESPONSES</span>';
|
|
96
|
-
if (w.startsWith("required_params_no_examples")) return
|
|
96
|
+
if (w.startsWith("required_params_no_examples")) return "";
|
|
97
97
|
return `<span class="warning-badge">${escapeHtml(w)}</span>`;
|
|
98
98
|
}).join(" ");
|
|
99
99
|
}
|
|
@@ -140,7 +140,8 @@ function renderEndpointDetail(ep: EndpointViewState): string {
|
|
|
140
140
|
|
|
141
141
|
return `<div class="covering-suite">
|
|
142
142
|
${icon}
|
|
143
|
-
<
|
|
143
|
+
<a class="suite-ref suite-link" href="#" data-suite="${escapeHtml(step.suiteName)}"
|
|
144
|
+
onclick="event.stopPropagation();switchToSuite(this.dataset.suite)">${escapeHtml(step.file)}</a>
|
|
144
145
|
<span class="dim" style="font-size:0.75rem;">→ "${escapeHtml(step.stepName)}"</span>
|
|
145
146
|
<span style="margin-left:auto;display:flex;align-items:center;gap:0.5rem;">${statusBadge}${duration}</span>
|
|
146
147
|
</div>${assertionsHtml}${hintHtml}`;
|
|
@@ -149,13 +150,16 @@ function renderEndpointDetail(ep: EndpointViewState): string {
|
|
|
149
150
|
}
|
|
150
151
|
|
|
151
152
|
// Fallback: just file names
|
|
152
|
-
const files = ep.coveringFiles.map(f =>
|
|
153
|
-
|
|
153
|
+
const files = ep.coveringFiles.map(f => {
|
|
154
|
+
const fileName = basename(f);
|
|
155
|
+
const suiteName = fileName.replace(/\.(ya?ml)$/i, "");
|
|
156
|
+
return `<div class="covering-suite">
|
|
154
157
|
<span class="step-icon" style="color:var(--text-dim);">○</span>
|
|
155
|
-
<
|
|
158
|
+
<a class="suite-ref suite-link" href="#" data-suite="${escapeHtml(suiteName)}"
|
|
159
|
+
onclick="event.stopPropagation();switchToSuite(this.dataset.suite)">${escapeHtml(fileName)}</a>
|
|
156
160
|
<span class="dim" style="font-size:0.75rem;">not run</span>
|
|
157
|
-
</div
|
|
158
|
-
).join("");
|
|
161
|
+
</div>`;
|
|
162
|
+
}).join("");
|
|
159
163
|
return files;
|
|
160
164
|
}
|
|
161
165
|
|