@testsmith/api-spector 0.3.9 → 0.4.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/out/main/chunks/{handle-Coax8Nz1.js → handle-BGYDylL2.js} +6 -0
- package/out/main/chunks/{import-Bilo697Z.js → import-CUcjlmSK.js} +2 -2
- package/out/main/chunks/{request-exec-C0DnGKdd.js → request-exec-DlvyG4rn.js} +1 -1
- package/out/main/chunks/{snapshots-CatB4zLh.js → snapshots-B7rDeWNy.js} +1 -1
- package/out/main/chunks/{soap-handler-B1Huudj7.js → soap-handler-CAyUjzxB.js} +2 -2
- package/out/main/contract.js +3 -3
- package/out/main/index.js +217 -18
- package/out/main/runner.js +3 -3
- package/out/main/wsdl.js +3 -3
- package/out/preload/index.js +9 -0
- package/out/renderer/assets/{index-BdUvP_cg.js → index-DGOQFW5S.js} +577 -82
- package/out/renderer/assets/index-DkZ0bxof.css +2 -0
- package/out/renderer/index.html +2 -2
- package/package.json +1 -1
- package/out/renderer/assets/index-B6kFQEbI.css +0 -2
|
@@ -13983,6 +13983,16 @@ const createCollectionsSlice = (set2, get2) => ({
|
|
|
13983
13983
|
if (folder.headers?.length) inheritedHeaders = [...inheritedHeaders, ...folder.headers];
|
|
13984
13984
|
}
|
|
13985
13985
|
return { auth: inheritedAuth, headers: inheritedHeaders };
|
|
13986
|
+
},
|
|
13987
|
+
getInheritedVariables: (requestId) => {
|
|
13988
|
+
const state = get2();
|
|
13989
|
+
const colEntry = Object.values(state.collections).find((c) => c.data.requests[requestId]);
|
|
13990
|
+
if (!colEntry) return {};
|
|
13991
|
+
const merged = {};
|
|
13992
|
+
for (const folder of findFolderPath(colEntry.data.rootFolder, requestId)) {
|
|
13993
|
+
if (folder.variables) Object.assign(merged, folder.variables);
|
|
13994
|
+
}
|
|
13995
|
+
return merged;
|
|
13986
13996
|
}
|
|
13987
13997
|
});
|
|
13988
13998
|
function envSlugRelPath(name2) {
|
|
@@ -14133,6 +14143,7 @@ const createUiSlice = (set2) => ({
|
|
|
14133
14143
|
pinnedResponse: null,
|
|
14134
14144
|
activeGitDiff: null,
|
|
14135
14145
|
quickInsertsOpen: true,
|
|
14146
|
+
sendSignal: 0,
|
|
14136
14147
|
setShowGeneratorPanel: (v) => set2((s) => {
|
|
14137
14148
|
s.showGeneratorPanel = v;
|
|
14138
14149
|
}),
|
|
@@ -14180,6 +14191,9 @@ const createUiSlice = (set2) => ({
|
|
|
14180
14191
|
}),
|
|
14181
14192
|
setQuickInsertsOpen: (open) => set2((s) => {
|
|
14182
14193
|
s.quickInsertsOpen = open;
|
|
14194
|
+
}),
|
|
14195
|
+
requestSend: () => set2((s) => {
|
|
14196
|
+
s.sendSignal += 1;
|
|
14183
14197
|
})
|
|
14184
14198
|
});
|
|
14185
14199
|
const useStore = create()(
|
|
@@ -35468,7 +35482,11 @@ function AuthEditor({
|
|
|
35468
35482
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35469
35483
|
"button",
|
|
35470
35484
|
{
|
|
35471
|
-
onClick: () =>
|
|
35485
|
+
onClick: () => {
|
|
35486
|
+
const ref2 = auth.passwordSecretRef ?? "NTLM_PASSWORD";
|
|
35487
|
+
onChange({ passwordSecretRef: ref2 });
|
|
35488
|
+
void secrets.saveSecret(ref2);
|
|
35489
|
+
},
|
|
35472
35490
|
className: "px-2 py-1 bg-blue-700 hover:bg-blue-600 rounded transition-colors",
|
|
35473
35491
|
children: secrets.saved ? "✓" : "Save"
|
|
35474
35492
|
}
|
|
@@ -35581,7 +35599,11 @@ function AuthEditor({
|
|
|
35581
35599
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35582
35600
|
"button",
|
|
35583
35601
|
{
|
|
35584
|
-
onClick: () =>
|
|
35602
|
+
onClick: () => {
|
|
35603
|
+
const ref2 = auth.apiKeySecretRef ?? "API_KEY";
|
|
35604
|
+
onChange({ apiKeySecretRef: ref2 });
|
|
35605
|
+
void secrets.saveSecret(ref2);
|
|
35606
|
+
},
|
|
35585
35607
|
className: "px-2 py-1 bg-blue-700 hover:bg-blue-600 rounded transition-colors",
|
|
35586
35608
|
children: secrets.saved ? "✓" : "Save"
|
|
35587
35609
|
}
|
|
@@ -35640,55 +35662,88 @@ function BasicCredentialsFields({
|
|
|
35640
35662
|
label,
|
|
35641
35663
|
note
|
|
35642
35664
|
}) {
|
|
35665
|
+
const [keychainOpen, setKeychainOpen] = reactExports.useState(!!auth.passwordSecretRef);
|
|
35643
35666
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
35644
35667
|
label && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-500 text-[10px] uppercase tracking-wide", children: label }),
|
|
35645
35668
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
|
|
35646
35669
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [
|
|
35647
35670
|
/* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-surface-400", children: "Username" }),
|
|
35648
35671
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35649
|
-
|
|
35672
|
+
VarInput,
|
|
35650
35673
|
{
|
|
35651
35674
|
value: auth.username ?? "",
|
|
35652
|
-
onChange: (
|
|
35675
|
+
onChange: (v) => setAuth({ username: v }),
|
|
35676
|
+
placeholder: "username",
|
|
35653
35677
|
className: "mt-1 w-full bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500"
|
|
35654
35678
|
}
|
|
35655
35679
|
)
|
|
35656
35680
|
] }),
|
|
35657
35681
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1", children: [
|
|
35658
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
35659
|
-
|
|
35660
|
-
|
|
35661
|
-
|
|
35662
|
-
|
|
35663
|
-
|
|
35664
|
-
|
|
35665
|
-
|
|
35666
|
-
|
|
35667
|
-
|
|
35668
|
-
|
|
35669
|
-
|
|
35670
|
-
|
|
35671
|
-
"
|
|
35672
|
-
|
|
35673
|
-
|
|
35674
|
-
|
|
35675
|
-
children: saved ? "✓" : "Save"
|
|
35676
|
-
}
|
|
35677
|
-
)
|
|
35678
|
-
] })
|
|
35682
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "text-surface-400", children: [
|
|
35683
|
+
"Password",
|
|
35684
|
+
" ",
|
|
35685
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-surface-500 text-[10px]", children: [
|
|
35686
|
+
"- supports ",
|
|
35687
|
+
"{{variables}}"
|
|
35688
|
+
] })
|
|
35689
|
+
] }),
|
|
35690
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35691
|
+
VarInput,
|
|
35692
|
+
{
|
|
35693
|
+
value: auth.password ?? "",
|
|
35694
|
+
onChange: (v) => setAuth({ password: v }),
|
|
35695
|
+
placeholder: "password or API token",
|
|
35696
|
+
className: "mt-1 w-full bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500 font-mono"
|
|
35697
|
+
}
|
|
35698
|
+
)
|
|
35679
35699
|
] })
|
|
35680
35700
|
] }),
|
|
35681
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
35682
|
-
"
|
|
35683
|
-
|
|
35684
|
-
|
|
35685
|
-
"
|
|
35686
|
-
|
|
35687
|
-
|
|
35688
|
-
|
|
35689
|
-
|
|
35690
|
-
|
|
35691
|
-
|
|
35701
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
35702
|
+
"button",
|
|
35703
|
+
{
|
|
35704
|
+
onClick: () => setKeychainOpen((o) => !o),
|
|
35705
|
+
className: "text-[10px] text-surface-500 hover:text-surface-300 text-left transition-colors w-fit",
|
|
35706
|
+
children: [
|
|
35707
|
+
keychainOpen ? "▾" : "▸",
|
|
35708
|
+
" Store password in OS keychain instead"
|
|
35709
|
+
]
|
|
35710
|
+
}
|
|
35711
|
+
),
|
|
35712
|
+
keychainOpen && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1 pl-3 border-l border-surface-800", children: [
|
|
35713
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-500 text-[10px]", children: "Encrypt the password in your OS keychain rather than saving it with the request. Leave the Password field above empty to use the keychain value." }),
|
|
35714
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
|
|
35715
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35716
|
+
"input",
|
|
35717
|
+
{
|
|
35718
|
+
type: "password",
|
|
35719
|
+
value: secretValue,
|
|
35720
|
+
onChange: (e) => setSecretValue(e.target.value),
|
|
35721
|
+
placeholder: auth.passwordSecretRef ? `Stored as "${auth.passwordSecretRef}"` : "Password",
|
|
35722
|
+
className: "flex-1 bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500 font-mono"
|
|
35723
|
+
}
|
|
35724
|
+
),
|
|
35725
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35726
|
+
"input",
|
|
35727
|
+
{
|
|
35728
|
+
value: auth.passwordSecretRef ?? "API_PASSWORD",
|
|
35729
|
+
onChange: (e) => setAuth({ passwordSecretRef: e.target.value }),
|
|
35730
|
+
placeholder: "Keychain key",
|
|
35731
|
+
className: "w-32 bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500"
|
|
35732
|
+
}
|
|
35733
|
+
),
|
|
35734
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35735
|
+
"button",
|
|
35736
|
+
{
|
|
35737
|
+
onClick: () => {
|
|
35738
|
+
const ref2 = auth.passwordSecretRef ?? "API_PASSWORD";
|
|
35739
|
+
setAuth({ passwordSecretRef: ref2 });
|
|
35740
|
+
void saveSecret(ref2);
|
|
35741
|
+
},
|
|
35742
|
+
className: "px-2 py-1 bg-blue-700 hover:bg-blue-600 rounded transition-colors",
|
|
35743
|
+
children: saved ? "✓" : "Save"
|
|
35744
|
+
}
|
|
35745
|
+
)
|
|
35746
|
+
] })
|
|
35692
35747
|
] }),
|
|
35693
35748
|
note && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-600 text-[10px]", children: note })
|
|
35694
35749
|
] });
|
|
@@ -35758,7 +35813,11 @@ function BearerPanel({
|
|
|
35758
35813
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35759
35814
|
"button",
|
|
35760
35815
|
{
|
|
35761
|
-
onClick: () =>
|
|
35816
|
+
onClick: () => {
|
|
35817
|
+
const ref2 = auth.tokenSecretRef ?? "API_TOKEN";
|
|
35818
|
+
setAuth({ tokenSecretRef: ref2 });
|
|
35819
|
+
void saveSecret(ref2);
|
|
35820
|
+
},
|
|
35762
35821
|
className: "px-2 py-1 bg-blue-700 hover:bg-blue-600 rounded transition-colors",
|
|
35763
35822
|
children: saved ? "✓" : "Save"
|
|
35764
35823
|
}
|
|
@@ -35772,11 +35831,17 @@ function FolderSettingsModal({ collectionId, folder, onClose }) {
|
|
|
35772
35831
|
const [activeTab, setActiveTab] = reactExports.useState("auth");
|
|
35773
35832
|
const [auth, setAuth] = reactExports.useState(folder.auth ?? { type: "none" });
|
|
35774
35833
|
const [headers, setHeaders] = reactExports.useState(folder.headers ?? []);
|
|
35834
|
+
const [varRows, setVarRows] = reactExports.useState(
|
|
35835
|
+
Object.entries(folder.variables ?? {}).map(([key, value]) => ({ key, value, enabled: true }))
|
|
35836
|
+
);
|
|
35775
35837
|
function patchAuth(patch) {
|
|
35776
35838
|
setAuth((prev) => ({ ...prev, ...patch }));
|
|
35777
35839
|
}
|
|
35778
35840
|
function save() {
|
|
35779
|
-
|
|
35841
|
+
const variables = Object.fromEntries(
|
|
35842
|
+
varRows.filter((r) => r.key.trim()).map((r) => [r.key.trim(), r.value])
|
|
35843
|
+
);
|
|
35844
|
+
updateFolder(collectionId, folder.id, { auth, headers, variables });
|
|
35780
35845
|
onClose();
|
|
35781
35846
|
}
|
|
35782
35847
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
@@ -35791,12 +35856,12 @@ function FolderSettingsModal({ collectionId, folder, onClose }) {
|
|
|
35791
35856
|
/* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-sm font-semibold", children: "Folder settings" }),
|
|
35792
35857
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[10px] text-surface-600 mt-0.5", children: [
|
|
35793
35858
|
folder.name,
|
|
35794
|
-
" - auth and
|
|
35859
|
+
" - auth, headers and variables inherited by all requests in this folder"
|
|
35795
35860
|
] })
|
|
35796
35861
|
] }),
|
|
35797
35862
|
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, className: "text-surface-400 hover:text-white text-lg leading-none", children: "×" })
|
|
35798
35863
|
] }),
|
|
35799
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex border-b border-surface-800 px-4 shrink-0", children: ["auth", "headers"].map((t2) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35864
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex border-b border-surface-800 px-4 shrink-0", children: ["auth", "headers", "variables"].map((t2) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35800
35865
|
"button",
|
|
35801
35866
|
{
|
|
35802
35867
|
onClick: () => setActiveTab(t2),
|
|
@@ -35823,7 +35888,23 @@ function FolderSettingsModal({ collectionId, folder, onClose }) {
|
|
|
35823
35888
|
valuePlaceholder: "value",
|
|
35824
35889
|
headerMode: true
|
|
35825
35890
|
}
|
|
35826
|
-
)
|
|
35891
|
+
),
|
|
35892
|
+
activeTab === "variables" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
35893
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[10px] text-surface-600", children: [
|
|
35894
|
+
"Variables scoped to this folder. They override collection variables and are overridden by an inner folder, the active environment, and script-set values. Reference them anywhere with ",
|
|
35895
|
+
"{{name}}",
|
|
35896
|
+
"."
|
|
35897
|
+
] }),
|
|
35898
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
35899
|
+
KVTable,
|
|
35900
|
+
{
|
|
35901
|
+
rows: varRows,
|
|
35902
|
+
onChange: setVarRows,
|
|
35903
|
+
keyPlaceholder: "VARIABLE_NAME",
|
|
35904
|
+
valuePlaceholder: "value"
|
|
35905
|
+
}
|
|
35906
|
+
)
|
|
35907
|
+
] })
|
|
35827
35908
|
] }),
|
|
35828
35909
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 px-4 py-3 border-t border-surface-800 shrink-0", children: [
|
|
35829
35910
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -55915,7 +55996,11 @@ function AuthTab({ request, onChange }) {
|
|
|
55915
55996
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
55916
55997
|
"button",
|
|
55917
55998
|
{
|
|
55918
|
-
onClick: () =>
|
|
55999
|
+
onClick: () => {
|
|
56000
|
+
const ref2 = auth.oauth2ClientSecretRef ?? "OAUTH2_CLIENT_SECRET";
|
|
56001
|
+
setAuth({ oauth2ClientSecretRef: ref2 });
|
|
56002
|
+
void saveSecret(ref2);
|
|
56003
|
+
},
|
|
55919
56004
|
className: "px-2 py-1 bg-blue-700 hover:bg-blue-600 rounded transition-colors",
|
|
55920
56005
|
children: saved ? "✓" : "Save"
|
|
55921
56006
|
}
|
|
@@ -63825,6 +63910,7 @@ function RequestBuilder({ request }) {
|
|
|
63825
63910
|
}
|
|
63826
63911
|
const [editingName, setEditingName] = reactExports.useState(false);
|
|
63827
63912
|
const [showFuzz, setShowFuzz] = reactExports.useState(false);
|
|
63913
|
+
const [customVerb, setCustomVerb] = reactExports.useState(false);
|
|
63828
63914
|
const [runHooks, setRunHooks] = reactExports.useState(() => localStorage.getItem("runHooks") !== "false");
|
|
63829
63915
|
function toggleRunHooks() {
|
|
63830
63916
|
setRunHooks((prev) => {
|
|
@@ -63836,6 +63922,14 @@ function RequestBuilder({ request }) {
|
|
|
63836
63922
|
function update(patch) {
|
|
63837
63923
|
updateRequest(request.id, patch);
|
|
63838
63924
|
}
|
|
63925
|
+
const sendSignal = useStore((s) => s.sendSignal);
|
|
63926
|
+
const lastSendSignal = reactExports.useRef(sendSignal);
|
|
63927
|
+
reactExports.useEffect(() => {
|
|
63928
|
+
if (sendSignal !== lastSendSignal.current) {
|
|
63929
|
+
lastSendSignal.current = sendSignal;
|
|
63930
|
+
void sendRequest();
|
|
63931
|
+
}
|
|
63932
|
+
}, [sendSignal]);
|
|
63839
63933
|
async function sendRequest() {
|
|
63840
63934
|
if (!activeTabId) return;
|
|
63841
63935
|
setTabSending(activeTabId, true);
|
|
@@ -63854,6 +63948,8 @@ function RequestBuilder({ request }) {
|
|
|
63854
63948
|
};
|
|
63855
63949
|
let collectionVars = {
|
|
63856
63950
|
...activeCollectionId ? collections[activeCollectionId]?.data.collectionVariables ?? {} : {},
|
|
63951
|
+
// Folder-chain variables sit above collection vars and below session/local.
|
|
63952
|
+
...useStore.getState().getInheritedVariables(request.id),
|
|
63857
63953
|
...sessionVars
|
|
63858
63954
|
};
|
|
63859
63955
|
let liveGlobals = { ...globals };
|
|
@@ -64064,15 +64160,39 @@ function RequestBuilder({ request }) {
|
|
|
64064
64160
|
}
|
|
64065
64161
|
)
|
|
64066
64162
|
] }),
|
|
64067
|
-
!isWs && !isSoap && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
64068
|
-
"
|
|
64163
|
+
!isWs && !isSoap && (customVerb ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
64164
|
+
"input",
|
|
64069
64165
|
{
|
|
64166
|
+
autoFocus: true,
|
|
64070
64167
|
value: request.method,
|
|
64071
|
-
onChange: (e) => update({ method: e.target.value }),
|
|
64072
|
-
|
|
64073
|
-
|
|
64168
|
+
onChange: (e) => update({ method: e.target.value.toUpperCase() }),
|
|
64169
|
+
onBlur: () => setCustomVerb(false),
|
|
64170
|
+
onKeyDown: (e) => {
|
|
64171
|
+
if (e.key === "Enter") {
|
|
64172
|
+
e.preventDefault();
|
|
64173
|
+
setCustomVerb(false);
|
|
64174
|
+
}
|
|
64175
|
+
},
|
|
64176
|
+
placeholder: "VERB",
|
|
64177
|
+
title: "Type any HTTP method",
|
|
64178
|
+
className: "w-24 bg-surface-800 border border-blue-500 rounded px-2 py-1.5 text-xs font-bold uppercase focus:outline-none text-fuchsia-400 placeholder-surface-600"
|
|
64074
64179
|
}
|
|
64075
|
-
)
|
|
64180
|
+
) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
64181
|
+
"select",
|
|
64182
|
+
{
|
|
64183
|
+
value: METHODS.includes(request.method) ? request.method : "__current__",
|
|
64184
|
+
onChange: (e) => {
|
|
64185
|
+
if (e.target.value === "__custom__") setCustomVerb(true);
|
|
64186
|
+
else if (e.target.value !== "__current__") update({ method: e.target.value });
|
|
64187
|
+
},
|
|
64188
|
+
className: `bg-surface-800 border border-surface-700 rounded px-2 py-1.5 text-xs font-bold focus:outline-none focus:border-blue-500 ${METHOD_COLORS[request.method] ?? "text-fuchsia-400"}`,
|
|
64189
|
+
children: [
|
|
64190
|
+
METHODS.map((m) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: m, className: "text-white", children: m }, m)),
|
|
64191
|
+
!METHODS.includes(request.method) && /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "__current__", className: "text-white", children: request.method }),
|
|
64192
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "__custom__", className: "text-white", children: "Custom…" })
|
|
64193
|
+
]
|
|
64194
|
+
}
|
|
64195
|
+
)),
|
|
64076
64196
|
isSoap && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
64077
64197
|
"span",
|
|
64078
64198
|
{
|
|
@@ -65248,7 +65368,7 @@ function KVBlock({ label, rows }) {
|
|
|
65248
65368
|
] }, k))
|
|
65249
65369
|
] });
|
|
65250
65370
|
}
|
|
65251
|
-
function HistoryTabRow({ entry, onLoad }) {
|
|
65371
|
+
function HistoryTabRow({ entry, onLoad, onResend }) {
|
|
65252
65372
|
const [open, setOpen] = reactExports.useState(false);
|
|
65253
65373
|
const reqBody = requestBodyText(entry.request.body);
|
|
65254
65374
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-b border-surface-800", children: [
|
|
@@ -65266,7 +65386,8 @@ function HistoryTabRow({ entry, onLoad }) {
|
|
|
65266
65386
|
] }),
|
|
65267
65387
|
entry.environmentName && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] bg-surface-800 text-surface-400 px-1.5 py-0.5 rounded shrink-0", children: entry.environmentName }),
|
|
65268
65388
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[11px] text-surface-500 ml-auto shrink-0", children: new Date(entry.timestamp).toLocaleString([], { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", second: "2-digit" }) }),
|
|
65269
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onLoad, title: "Load this response into the viewer", className: "text-[10px] text-blue-400 hover:text-blue-300 shrink-0", children: "load" })
|
|
65389
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onLoad, title: "Load this response into the viewer", className: "text-[10px] text-blue-400 hover:text-blue-300 shrink-0", children: "load" }),
|
|
65390
|
+
onResend && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onResend, title: "Send this request again", className: "text-[10px] text-emerald-400 hover:text-emerald-300 shrink-0", children: "resend" })
|
|
65270
65391
|
] }),
|
|
65271
65392
|
open && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 pb-3 pt-1 flex flex-col gap-3 bg-surface-950/40", children: [
|
|
65272
65393
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
@@ -65307,6 +65428,7 @@ function ResponseViewer() {
|
|
|
65307
65428
|
const hookResults = activeTab?.lastHookResults ?? null;
|
|
65308
65429
|
const requestId = activeTab?.requestId ?? null;
|
|
65309
65430
|
const setTabResponse = useStore((s) => s.setTabResponse);
|
|
65431
|
+
const requestSend = useStore((s) => s.requestSend);
|
|
65310
65432
|
const history2 = useStore((s) => s.history);
|
|
65311
65433
|
const activeEnvironmentId = useStore((s) => s.activeEnvironmentId);
|
|
65312
65434
|
const environments = useStore((s) => s.environments);
|
|
@@ -65330,6 +65452,13 @@ function ResponseViewer() {
|
|
|
65330
65452
|
setTab("console");
|
|
65331
65453
|
}
|
|
65332
65454
|
}, [scriptResult?.preScriptError, scriptResult?.postScriptError]);
|
|
65455
|
+
reactExports.useEffect(() => {
|
|
65456
|
+
if (response?.error) {
|
|
65457
|
+
if (tab !== "request" && tab !== "history") setTab("error");
|
|
65458
|
+
} else if (tab === "error") {
|
|
65459
|
+
setTab("body");
|
|
65460
|
+
}
|
|
65461
|
+
}, [response]);
|
|
65333
65462
|
const [diffMode, setDiffMode] = reactExports.useState(false);
|
|
65334
65463
|
const [showMockModal, setShowMockModal] = reactExports.useState(false);
|
|
65335
65464
|
const [bodyView, setBodyView] = reactExports.useState("raw");
|
|
@@ -65370,12 +65499,6 @@ function ResponseViewer() {
|
|
|
65370
65499
|
if (!response) {
|
|
65371
65500
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-full flex items-center justify-center text-surface-400 text-sm", children: "Hit Send to see the response" });
|
|
65372
65501
|
}
|
|
65373
|
-
if (response.error) {
|
|
65374
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col p-4 gap-2", children: [
|
|
65375
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-red-400 text-sm font-medium", children: "Request failed" }),
|
|
65376
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "text-xs text-red-300 whitespace-pre-wrap", children: response.error })
|
|
65377
|
-
] });
|
|
65378
|
-
}
|
|
65379
65502
|
const contentType = response.headers["content-type"] ?? "";
|
|
65380
65503
|
const isJson = contentType.includes("json");
|
|
65381
65504
|
const isXml = !isJson && (contentType.includes("xml") || contentType.includes("html"));
|
|
@@ -65393,15 +65516,31 @@ function ResponseViewer() {
|
|
|
65393
65516
|
const totalCount = scriptResult?.testResults.length ?? 0;
|
|
65394
65517
|
const consoleCount = scriptResult?.consoleOutput.length ?? 0;
|
|
65395
65518
|
const hasScriptError = !!(scriptResult?.preScriptError || scriptResult?.postScriptError);
|
|
65396
|
-
const
|
|
65519
|
+
const historyBadge = requestHistory.length > 0 ? requestHistory.length : void 0;
|
|
65520
|
+
const tabList = response.error ? [
|
|
65521
|
+
{ id: "error", label: "Error", error: true },
|
|
65522
|
+
{ id: "request", label: "Request" },
|
|
65523
|
+
{ id: "history", label: "History", badge: historyBadge }
|
|
65524
|
+
] : [
|
|
65397
65525
|
{ id: "request", label: "Request" },
|
|
65398
65526
|
{ id: "body", label: "Body", badge: bodyParseError ? "!" : void 0, error: bodyParseError },
|
|
65399
65527
|
{ id: "headers", label: "Headers" },
|
|
65400
65528
|
{ id: "tests", label: "Tests", badge: totalCount > 0 ? `${passedCount}/${totalCount}` : void 0 },
|
|
65401
65529
|
{ id: "console", label: "Console", badge: hasScriptError ? "!" : consoleCount > 0 ? consoleCount : void 0, error: hasScriptError },
|
|
65402
|
-
{ id: "history", label: "History", badge:
|
|
65530
|
+
{ id: "history", label: "History", badge: historyBadge },
|
|
65403
65531
|
{ id: "http", label: "HTTP", badge: httpFindings.length > 0 ? httpErrors > 0 ? "!" : httpFindings.length : void 0, error: httpErrors > 0 }
|
|
65404
65532
|
];
|
|
65533
|
+
const historyContent = requestHistory.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-surface-500 text-center p-8", children: "No past responses for this request yet. Each send is recorded here." }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-col", children: requestHistory.map((entry) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
65534
|
+
HistoryTabRow,
|
|
65535
|
+
{
|
|
65536
|
+
entry,
|
|
65537
|
+
onLoad: () => {
|
|
65538
|
+
if (activeTabId) setTabResponse(activeTabId, entry.response, entry.scriptResult ?? null);
|
|
65539
|
+
},
|
|
65540
|
+
onResend: requestSend
|
|
65541
|
+
},
|
|
65542
|
+
entry.id
|
|
65543
|
+
)) });
|
|
65405
65544
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col", children: [
|
|
65406
65545
|
hookResults && hookResults.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(HookResultsPanel, { results: hookResults }),
|
|
65407
65546
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4 px-4 py-1.5 border-b border-surface-800 flex-shrink-0 overflow-x-auto", children: [
|
|
@@ -65430,7 +65569,7 @@ function ResponseViewer() {
|
|
|
65430
65569
|
},
|
|
65431
65570
|
t2.id
|
|
65432
65571
|
)) }),
|
|
65433
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ml-auto flex items-center gap-1 shrink-0", children: [
|
|
65572
|
+
!response.error && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ml-auto flex items-center gap-1 shrink-0", children: [
|
|
65434
65573
|
assertToast.toast && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-emerald-400 font-medium px-1", children: assertToast.toast.msg }),
|
|
65435
65574
|
contractToast.toast && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-blue-400 font-medium px-1", children: contractToast.toast.msg }),
|
|
65436
65575
|
tab === "body" && supportsTree && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex rounded overflow-hidden border border-surface-800 mr-1", children: [
|
|
@@ -65492,7 +65631,10 @@ function ResponseViewer() {
|
|
|
65492
65631
|
] })
|
|
65493
65632
|
] }),
|
|
65494
65633
|
showMockModal && /* @__PURE__ */ jsxRuntimeExports.jsx(SaveAsMockModal, { onClose: () => setShowMockModal(false) }),
|
|
65495
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0 flex flex-col overflow-y-auto", children:
|
|
65634
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0 flex flex-col overflow-y-auto", children: response.error ? tab === "request" ? /* @__PURE__ */ jsxRuntimeExports.jsx(RequestPanel, { sentRequest }) : tab === "history" ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: historyContent }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col p-4 gap-2", children: [
|
|
65635
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-red-400 text-sm font-medium", children: "Request failed" }),
|
|
65636
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "text-xs text-red-300 whitespace-pre-wrap", children: response.error })
|
|
65637
|
+
] }) : diffMode && pinnedResponse ? /* @__PURE__ */ jsxRuntimeExports.jsx(DiffView, { pinned: pinnedResponse, current: response }) : tab === "body" && supportsTree && bodyView === "tree" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
65496
65638
|
InteractiveBody,
|
|
65497
65639
|
{
|
|
65498
65640
|
body: response.body,
|
|
@@ -65538,16 +65680,7 @@ function ResponseViewer() {
|
|
|
65538
65680
|
] }),
|
|
65539
65681
|
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-surface-200 mt-1", children: find2.message })
|
|
65540
65682
|
] }, i);
|
|
65541
|
-
}) }) }) : tab === "history" ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children:
|
|
65542
|
-
HistoryTabRow,
|
|
65543
|
-
{
|
|
65544
|
-
entry,
|
|
65545
|
-
onLoad: () => {
|
|
65546
|
-
if (activeTabId) setTabResponse(activeTabId, entry.response, entry.scriptResult ?? null);
|
|
65547
|
-
}
|
|
65548
|
-
},
|
|
65549
|
-
entry.id
|
|
65550
|
-
)) }) }) : null }),
|
|
65683
|
+
}) }) }) : tab === "history" ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: historyContent }) : null }),
|
|
65551
65684
|
headerMenu && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
65552
65685
|
ContextMenu,
|
|
65553
65686
|
{
|
|
@@ -65632,7 +65765,8 @@ const TARGETS = [
|
|
|
65632
65765
|
{ id: "supertest_js", label: "Supertest JS", description: "Jest + Supertest JavaScript tests" },
|
|
65633
65766
|
{ id: "rest_assured", label: "REST Assured", description: "Java + JUnit 5 + Maven pom.xml" },
|
|
65634
65767
|
{ id: "karate", label: "Karate", description: "Karate feature files + JUnit 5 runner + Maven" },
|
|
65635
|
-
{ id: "http_file", label: "HTTP file", description: ".http / .rest file (VSCode REST Client / IntelliJ)" }
|
|
65768
|
+
{ id: "http_file", label: "HTTP file", description: ".http / .rest file (VSCode REST Client / IntelliJ)" },
|
|
65769
|
+
{ id: "curl", label: "cURL", description: "Runnable shell script, one curl command per request" }
|
|
65636
65770
|
];
|
|
65637
65771
|
function GeneratorPanel() {
|
|
65638
65772
|
const setShowGeneratorPanel = useStore((s) => s.setShowGeneratorPanel);
|
|
@@ -65888,6 +66022,7 @@ function HistoryPanel() {
|
|
|
65888
66022
|
const activeTabId = useStore((s) => s.activeTabId);
|
|
65889
66023
|
const setTabResponse = useStore((s) => s.setTabResponse);
|
|
65890
66024
|
const setActiveRequest = useStore((s) => s.setActiveRequest);
|
|
66025
|
+
const requestSend = useStore((s) => s.requestSend);
|
|
65891
66026
|
const collections = useStore((s) => s.collections);
|
|
65892
66027
|
const [selected, setSelected] = reactExports.useState(null);
|
|
65893
66028
|
const [search, setSearch] = reactExports.useState("");
|
|
@@ -65910,8 +66045,8 @@ function HistoryPanel() {
|
|
|
65910
66045
|
}
|
|
65911
66046
|
function open(entry) {
|
|
65912
66047
|
setSelected(entry);
|
|
65913
|
-
const
|
|
65914
|
-
if (
|
|
66048
|
+
const stillExists2 = Object.values(collections).some((c) => entry.request.id in c.data.requests);
|
|
66049
|
+
if (stillExists2) {
|
|
65915
66050
|
setActiveRequest(entry.request.id);
|
|
65916
66051
|
const tabId = useStore.getState().activeTabId;
|
|
65917
66052
|
if (tabId) setTabResponse(tabId, entry.response, entry.scriptResult ?? null);
|
|
@@ -65919,6 +66054,14 @@ function HistoryPanel() {
|
|
|
65919
66054
|
setTabResponse(activeTabId, entry.response, entry.scriptResult ?? null);
|
|
65920
66055
|
}
|
|
65921
66056
|
}
|
|
66057
|
+
function replay(entry) {
|
|
66058
|
+
setSelected(entry);
|
|
66059
|
+
setActiveRequest(entry.request.id);
|
|
66060
|
+
requestSend();
|
|
66061
|
+
}
|
|
66062
|
+
function stillExists(entry) {
|
|
66063
|
+
return Object.values(collections).some((c) => entry.request.id in c.data.requests);
|
|
66064
|
+
}
|
|
65922
66065
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col flex-1 min-h-0", children: [
|
|
65923
66066
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-2 py-2 border-b border-surface-800 flex gap-1.5 flex-shrink-0", children: [
|
|
65924
66067
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -65963,7 +66106,8 @@ function HistoryPanel() {
|
|
|
65963
66106
|
{
|
|
65964
66107
|
entry,
|
|
65965
66108
|
isSelected: selected?.id === entry.id,
|
|
65966
|
-
onSelect: () => open(entry)
|
|
66109
|
+
onSelect: () => open(entry),
|
|
66110
|
+
onResend: stillExists(entry) ? () => replay(entry) : void 0
|
|
65967
66111
|
},
|
|
65968
66112
|
entry.id
|
|
65969
66113
|
))
|
|
@@ -65974,19 +66118,40 @@ function HistoryPanel() {
|
|
|
65974
66118
|
function HistoryRow({
|
|
65975
66119
|
entry,
|
|
65976
66120
|
isSelected,
|
|
65977
|
-
onSelect
|
|
66121
|
+
onSelect,
|
|
66122
|
+
onResend
|
|
65978
66123
|
}) {
|
|
65979
66124
|
const status = entry.response.status;
|
|
65980
66125
|
const hasError = !!entry.response.error;
|
|
65981
66126
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
65982
|
-
"
|
|
66127
|
+
"div",
|
|
65983
66128
|
{
|
|
66129
|
+
role: "button",
|
|
66130
|
+
tabIndex: 0,
|
|
65984
66131
|
onClick: onSelect,
|
|
65985
|
-
|
|
66132
|
+
onKeyDown: (e) => {
|
|
66133
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
66134
|
+
e.preventDefault();
|
|
66135
|
+
onSelect();
|
|
66136
|
+
}
|
|
66137
|
+
},
|
|
66138
|
+
className: `group w-full text-left px-3 py-2 border-b border-surface-800/50 transition-colors cursor-pointer ${isSelected ? "bg-surface-800" : "hover:bg-surface-800/50"}`,
|
|
65986
66139
|
children: [
|
|
65987
66140
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
|
|
65988
66141
|
/* @__PURE__ */ jsxRuntimeExports.jsx(MethodBadge, { method: entry.request.method, size: "xs" }),
|
|
65989
66142
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 text-xs truncate text-white", children: entry.request.name }),
|
|
66143
|
+
onResend && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
66144
|
+
"button",
|
|
66145
|
+
{
|
|
66146
|
+
onClick: (e) => {
|
|
66147
|
+
e.stopPropagation();
|
|
66148
|
+
onResend();
|
|
66149
|
+
},
|
|
66150
|
+
title: "Send this request again",
|
|
66151
|
+
className: "text-[10px] text-emerald-400 hover:text-emerald-300 shrink-0 opacity-0 group-hover:opacity-100 transition-opacity",
|
|
66152
|
+
children: "resend"
|
|
66153
|
+
}
|
|
66154
|
+
),
|
|
65990
66155
|
hasError ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-red-400 text-[10px] font-medium shrink-0", children: "ERR" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] font-bold shrink-0 ${statusColor$2(status)}`, children: status })
|
|
65991
66156
|
] }),
|
|
65992
66157
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mt-0.5 min-w-0", children: [
|
|
@@ -66010,6 +66175,15 @@ function HistoryRow({
|
|
|
66010
66175
|
const { electron: electron$f } = window;
|
|
66011
66176
|
function WelcomeScreen() {
|
|
66012
66177
|
const { applyWorkspace } = useWorkspaceLoader();
|
|
66178
|
+
const [recents, setRecents] = reactExports.useState([]);
|
|
66179
|
+
const [update, setUpdate] = reactExports.useState(null);
|
|
66180
|
+
reactExports.useEffect(() => {
|
|
66181
|
+
electron$f.getRecentWorkspaces().then(setRecents).catch(() => setRecents([]));
|
|
66182
|
+
electron$f.checkForUpdate().then((info) => {
|
|
66183
|
+
if (info?.updateAvailable) setUpdate(info);
|
|
66184
|
+
}).catch(() => {
|
|
66185
|
+
});
|
|
66186
|
+
}, []);
|
|
66013
66187
|
async function openWorkspace() {
|
|
66014
66188
|
const result = await electron$f.openWorkspace();
|
|
66015
66189
|
if (!result) return;
|
|
@@ -66020,7 +66194,25 @@ function WelcomeScreen() {
|
|
|
66020
66194
|
if (!result) return;
|
|
66021
66195
|
await applyWorkspace(result.workspace, result.workspacePath);
|
|
66022
66196
|
}
|
|
66197
|
+
async function openRecent(path) {
|
|
66198
|
+
const result = await electron$f.openWorkspacePath(path);
|
|
66199
|
+
if (!result) {
|
|
66200
|
+
setRecents((prev) => prev.filter((r) => r.path !== path));
|
|
66201
|
+
return;
|
|
66202
|
+
}
|
|
66203
|
+
await applyWorkspace(result.workspace, result.workspacePath);
|
|
66204
|
+
}
|
|
66023
66205
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col items-center justify-center gap-6 text-center p-8", children: [
|
|
66206
|
+
update && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center gap-1 px-4 py-2 rounded-lg border border-blue-800 bg-blue-950/40 text-xs", children: [
|
|
66207
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-200", children: [
|
|
66208
|
+
"New version available (v",
|
|
66209
|
+
update.latest,
|
|
66210
|
+
", you have v",
|
|
66211
|
+
update.current,
|
|
66212
|
+
")"
|
|
66213
|
+
] }),
|
|
66214
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "text-[11px] text-blue-300 bg-surface-900 px-2 py-0.5 rounded select-all", children: update.command })
|
|
66215
|
+
] }),
|
|
66024
66216
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
|
|
66025
66217
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("h1", { className: "text-2xl font-semibold mb-1", children: [
|
|
66026
66218
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "var(--wordmark-muted)" }, children: "API" }),
|
|
@@ -66040,7 +66232,11 @@ function WelcomeScreen() {
|
|
|
66040
66232
|
}
|
|
66041
66233
|
)
|
|
66042
66234
|
] }),
|
|
66043
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-400 text-sm max-w-sm", children: "Local-first API testing with Robot Framework & Playwright code generation. Secrets stay on your machine." })
|
|
66235
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-400 text-sm max-w-sm", children: "Local-first API testing with Robot Framework & Playwright code generation. Secrets stay on your machine." }),
|
|
66236
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[11px] mt-3", style: { color: "var(--text-muted)" }, children: [
|
|
66237
|
+
"version ",
|
|
66238
|
+
"0.4.1"
|
|
66239
|
+
] })
|
|
66044
66240
|
] }),
|
|
66045
66241
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-3 w-64", children: [
|
|
66046
66242
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -66060,6 +66256,22 @@ function WelcomeScreen() {
|
|
|
66060
66256
|
}
|
|
66061
66257
|
)
|
|
66062
66258
|
] }),
|
|
66259
|
+
recents.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1 w-72 text-left", children: [
|
|
66260
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] uppercase tracking-wider font-semibold px-1", style: { color: "var(--text-muted)" }, children: "Recent workspaces" }),
|
|
66261
|
+
recents.map((r) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
66262
|
+
"button",
|
|
66263
|
+
{
|
|
66264
|
+
onClick: () => openRecent(r.path),
|
|
66265
|
+
title: r.path,
|
|
66266
|
+
className: "flex flex-col items-start px-2 py-1.5 rounded hover:bg-surface-800 transition-colors text-left group",
|
|
66267
|
+
children: [
|
|
66268
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-surface-200 group-hover:text-white truncate max-w-full", children: r.name }),
|
|
66269
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-surface-500 truncate max-w-full", children: r.path })
|
|
66270
|
+
]
|
|
66271
|
+
},
|
|
66272
|
+
r.path
|
|
66273
|
+
))
|
|
66274
|
+
] }),
|
|
66063
66275
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-surface-400 text-xs max-w-xs", children: [
|
|
66064
66276
|
"A workspace is a ",
|
|
66065
66277
|
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "text-surface-500", children: ".spector" }),
|
|
@@ -67256,6 +67468,230 @@ function DocsGeneratorModal({ onClose }) {
|
|
|
67256
67468
|
}
|
|
67257
67469
|
);
|
|
67258
67470
|
}
|
|
67471
|
+
const SKIP_WITH_ARG = /* @__PURE__ */ new Set([
|
|
67472
|
+
"--connect-timeout",
|
|
67473
|
+
"--max-time",
|
|
67474
|
+
"-m",
|
|
67475
|
+
"--retry",
|
|
67476
|
+
"--resolve",
|
|
67477
|
+
"--cacert",
|
|
67478
|
+
"--cert",
|
|
67479
|
+
"--key",
|
|
67480
|
+
"--proxy",
|
|
67481
|
+
"-x",
|
|
67482
|
+
"--output",
|
|
67483
|
+
"-o",
|
|
67484
|
+
"--write-out",
|
|
67485
|
+
"-w"
|
|
67486
|
+
]);
|
|
67487
|
+
function tokenize(input) {
|
|
67488
|
+
const tokens = [];
|
|
67489
|
+
let cur2 = "";
|
|
67490
|
+
let inTok = false;
|
|
67491
|
+
let i = 0;
|
|
67492
|
+
const n = input.length;
|
|
67493
|
+
while (i < n) {
|
|
67494
|
+
const c = input[i];
|
|
67495
|
+
if (c === "\\" && (input[i + 1] === "\n" || input[i + 1] === "\r")) {
|
|
67496
|
+
i += input[i + 1] === "\r" && input[i + 2] === "\n" ? 3 : 2;
|
|
67497
|
+
continue;
|
|
67498
|
+
}
|
|
67499
|
+
if (c === "'") {
|
|
67500
|
+
inTok = true;
|
|
67501
|
+
i++;
|
|
67502
|
+
while (i < n && input[i] !== "'") cur2 += input[i++];
|
|
67503
|
+
i++;
|
|
67504
|
+
continue;
|
|
67505
|
+
}
|
|
67506
|
+
if (c === '"') {
|
|
67507
|
+
inTok = true;
|
|
67508
|
+
i++;
|
|
67509
|
+
while (i < n && input[i] !== '"') {
|
|
67510
|
+
if (input[i] === "\\" && i + 1 < n && `"\\$\``.includes(input[i + 1])) {
|
|
67511
|
+
cur2 += input[i + 1];
|
|
67512
|
+
i += 2;
|
|
67513
|
+
} else cur2 += input[i++];
|
|
67514
|
+
}
|
|
67515
|
+
i++;
|
|
67516
|
+
continue;
|
|
67517
|
+
}
|
|
67518
|
+
if (/\s/.test(c)) {
|
|
67519
|
+
if (inTok) {
|
|
67520
|
+
tokens.push(cur2);
|
|
67521
|
+
cur2 = "";
|
|
67522
|
+
inTok = false;
|
|
67523
|
+
}
|
|
67524
|
+
i++;
|
|
67525
|
+
continue;
|
|
67526
|
+
}
|
|
67527
|
+
if (c === "\\" && i + 1 < n) {
|
|
67528
|
+
cur2 += input[i + 1];
|
|
67529
|
+
i += 2;
|
|
67530
|
+
inTok = true;
|
|
67531
|
+
continue;
|
|
67532
|
+
}
|
|
67533
|
+
cur2 += c;
|
|
67534
|
+
inTok = true;
|
|
67535
|
+
i++;
|
|
67536
|
+
}
|
|
67537
|
+
if (inTok) tokens.push(cur2);
|
|
67538
|
+
return tokens;
|
|
67539
|
+
}
|
|
67540
|
+
function looksJson(s) {
|
|
67541
|
+
const t2 = s.trim();
|
|
67542
|
+
if (!(t2.startsWith("{") || t2.startsWith("["))) return false;
|
|
67543
|
+
try {
|
|
67544
|
+
JSON.parse(t2);
|
|
67545
|
+
return true;
|
|
67546
|
+
} catch {
|
|
67547
|
+
return false;
|
|
67548
|
+
}
|
|
67549
|
+
}
|
|
67550
|
+
function decodeBasic(b642) {
|
|
67551
|
+
try {
|
|
67552
|
+
const decoded = typeof atob === "function" ? atob(b642) : Buffer.from(b642, "base64").toString("utf8");
|
|
67553
|
+
const idx = decoded.indexOf(":");
|
|
67554
|
+
if (idx === -1) return null;
|
|
67555
|
+
return { username: decoded.slice(0, idx), password: decoded.slice(idx + 1) };
|
|
67556
|
+
} catch {
|
|
67557
|
+
return null;
|
|
67558
|
+
}
|
|
67559
|
+
}
|
|
67560
|
+
function parseCurl(command2) {
|
|
67561
|
+
const tokens = tokenize(command2.trim());
|
|
67562
|
+
if (tokens[0] === "curl") tokens.shift();
|
|
67563
|
+
let method = null;
|
|
67564
|
+
let url = "";
|
|
67565
|
+
let user = null;
|
|
67566
|
+
let forceGet = false;
|
|
67567
|
+
const headers = [];
|
|
67568
|
+
const dataParts = [];
|
|
67569
|
+
const formParts = [];
|
|
67570
|
+
const next = (i) => tokens[i + 1] ?? "";
|
|
67571
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
67572
|
+
const t2 = tokens[i];
|
|
67573
|
+
switch (t2) {
|
|
67574
|
+
case "-X":
|
|
67575
|
+
case "--request":
|
|
67576
|
+
method = next(i);
|
|
67577
|
+
i++;
|
|
67578
|
+
break;
|
|
67579
|
+
case "-H":
|
|
67580
|
+
case "--header": {
|
|
67581
|
+
const raw = next(i);
|
|
67582
|
+
i++;
|
|
67583
|
+
const idx = raw.indexOf(":");
|
|
67584
|
+
if (idx > 0) headers.push({ key: raw.slice(0, idx).trim(), value: raw.slice(idx + 1).trim(), enabled: true });
|
|
67585
|
+
break;
|
|
67586
|
+
}
|
|
67587
|
+
case "-u":
|
|
67588
|
+
case "--user":
|
|
67589
|
+
user = next(i);
|
|
67590
|
+
i++;
|
|
67591
|
+
break;
|
|
67592
|
+
case "-d":
|
|
67593
|
+
case "--data":
|
|
67594
|
+
case "--data-raw":
|
|
67595
|
+
case "--data-ascii":
|
|
67596
|
+
case "--data-binary":
|
|
67597
|
+
dataParts.push(next(i));
|
|
67598
|
+
i++;
|
|
67599
|
+
break;
|
|
67600
|
+
case "--data-urlencode":
|
|
67601
|
+
dataParts.push(next(i));
|
|
67602
|
+
i++;
|
|
67603
|
+
break;
|
|
67604
|
+
case "-F":
|
|
67605
|
+
case "--form":
|
|
67606
|
+
formParts.push(next(i));
|
|
67607
|
+
i++;
|
|
67608
|
+
break;
|
|
67609
|
+
case "-b":
|
|
67610
|
+
case "--cookie":
|
|
67611
|
+
headers.push({ key: "Cookie", value: next(i), enabled: true });
|
|
67612
|
+
i++;
|
|
67613
|
+
break;
|
|
67614
|
+
case "-A":
|
|
67615
|
+
case "--user-agent":
|
|
67616
|
+
headers.push({ key: "User-Agent", value: next(i), enabled: true });
|
|
67617
|
+
i++;
|
|
67618
|
+
break;
|
|
67619
|
+
case "-e":
|
|
67620
|
+
case "--referer":
|
|
67621
|
+
headers.push({ key: "Referer", value: next(i), enabled: true });
|
|
67622
|
+
i++;
|
|
67623
|
+
break;
|
|
67624
|
+
case "--url":
|
|
67625
|
+
url = next(i);
|
|
67626
|
+
i++;
|
|
67627
|
+
break;
|
|
67628
|
+
case "-G":
|
|
67629
|
+
case "--get":
|
|
67630
|
+
forceGet = true;
|
|
67631
|
+
break;
|
|
67632
|
+
case "-I":
|
|
67633
|
+
case "--head":
|
|
67634
|
+
method = method ?? "HEAD";
|
|
67635
|
+
break;
|
|
67636
|
+
default:
|
|
67637
|
+
if (SKIP_WITH_ARG.has(t2)) {
|
|
67638
|
+
i++;
|
|
67639
|
+
break;
|
|
67640
|
+
}
|
|
67641
|
+
if (t2.startsWith("-")) break;
|
|
67642
|
+
if (!url) url = t2;
|
|
67643
|
+
break;
|
|
67644
|
+
}
|
|
67645
|
+
}
|
|
67646
|
+
if (!url) throw new Error("No URL found in the curl command.");
|
|
67647
|
+
const hasData = dataParts.length > 0;
|
|
67648
|
+
const isForm = formParts.length > 0;
|
|
67649
|
+
const resolvedMethod = (forceGet ? "GET" : method ?? (hasData || isForm ? "POST" : "GET")).toUpperCase();
|
|
67650
|
+
let auth = { type: "none" };
|
|
67651
|
+
if (user) {
|
|
67652
|
+
const idx = user.indexOf(":");
|
|
67653
|
+
auth = { type: "basic", username: idx === -1 ? user : user.slice(0, idx), password: idx === -1 ? "" : user.slice(idx + 1) };
|
|
67654
|
+
} else {
|
|
67655
|
+
const authIdx = headers.findIndex((h) => h.key.toLowerCase() === "authorization");
|
|
67656
|
+
if (authIdx !== -1) {
|
|
67657
|
+
const v = headers[authIdx].value;
|
|
67658
|
+
if (/^Bearer\s+/i.test(v)) {
|
|
67659
|
+
auth = { type: "bearer", token: v.replace(/^Bearer\s+/i, "") };
|
|
67660
|
+
headers.splice(authIdx, 1);
|
|
67661
|
+
} else if (/^Basic\s+/i.test(v)) {
|
|
67662
|
+
const creds = decodeBasic(v.replace(/^Basic\s+/i, "").trim());
|
|
67663
|
+
if (creds) {
|
|
67664
|
+
auth = { type: "basic", ...creds };
|
|
67665
|
+
headers.splice(authIdx, 1);
|
|
67666
|
+
}
|
|
67667
|
+
}
|
|
67668
|
+
}
|
|
67669
|
+
}
|
|
67670
|
+
let body = { mode: "none" };
|
|
67671
|
+
if (isForm) {
|
|
67672
|
+
body = {
|
|
67673
|
+
mode: "form",
|
|
67674
|
+
form: formParts.map((kv) => {
|
|
67675
|
+
const idx = kv.indexOf("=");
|
|
67676
|
+
return { key: idx === -1 ? kv : kv.slice(0, idx), value: idx === -1 ? "" : kv.slice(idx + 1), enabled: true };
|
|
67677
|
+
})
|
|
67678
|
+
};
|
|
67679
|
+
} else if (hasData) {
|
|
67680
|
+
const data = dataParts.join("&");
|
|
67681
|
+
const ct = headers.find((h) => h.key.toLowerCase() === "content-type")?.value;
|
|
67682
|
+
if (ct && ct.includes("json") || looksJson(data)) body = { mode: "json", json: data };
|
|
67683
|
+
else body = { mode: "raw", raw: data, rawContentType: ct ?? "application/x-www-form-urlencoded" };
|
|
67684
|
+
}
|
|
67685
|
+
let name2;
|
|
67686
|
+
try {
|
|
67687
|
+
const u = new URL(url.replace(/\{\{[^}]+\}\}/g, "x"));
|
|
67688
|
+
name2 = `${resolvedMethod} ${u.pathname === "/" ? u.hostname : u.pathname}`;
|
|
67689
|
+
} catch {
|
|
67690
|
+
const cleaned = url.replace(/^[a-z]+:\/\//i, "").replace(/\?.*$/, "");
|
|
67691
|
+
name2 = `${resolvedMethod} ${cleaned}`.trim();
|
|
67692
|
+
}
|
|
67693
|
+
return { name: name2, method: resolvedMethod, url, headers, params: [], auth, body };
|
|
67694
|
+
}
|
|
67259
67695
|
const { electron: electron$9 } = window;
|
|
67260
67696
|
const OPTIONS = [
|
|
67261
67697
|
{ id: "postman", label: "Postman", description: "Collection v2.1 JSON" },
|
|
@@ -67263,7 +67699,8 @@ const OPTIONS = [
|
|
|
67263
67699
|
{ id: "insomnia", label: "Insomnia", description: "Export v4 JSON" },
|
|
67264
67700
|
{ id: "bruno", label: "Bruno", description: "bruno.json collection file" },
|
|
67265
67701
|
{ id: "http", label: "HTTP file", description: ".http / .rest (REST Client)" },
|
|
67266
|
-
{ id: "spector", label: "API Spector", description: "An existing .spector / .json collection" }
|
|
67702
|
+
{ id: "spector", label: "API Spector", description: "An existing .spector / .json collection" },
|
|
67703
|
+
{ id: "curl", label: "cURL", description: "Paste a curl command" }
|
|
67267
67704
|
];
|
|
67268
67705
|
function listEndpoints(col) {
|
|
67269
67706
|
const result = [];
|
|
@@ -67287,6 +67724,7 @@ function ImportModal({ onImport, onClose }) {
|
|
|
67287
67724
|
const markCollectionClean = useStore((s) => s.markCollectionClean);
|
|
67288
67725
|
const [selected, setSelected] = reactExports.useState(null);
|
|
67289
67726
|
const [url, setUrl] = reactExports.useState("");
|
|
67727
|
+
const [curlText, setCurlText] = reactExports.useState("");
|
|
67290
67728
|
const [loading, setLoading] = reactExports.useState(false);
|
|
67291
67729
|
const [error2, setError] = reactExports.useState(null);
|
|
67292
67730
|
const urlInputRef = reactExports.useRef(null);
|
|
@@ -67365,6 +67803,37 @@ function ImportModal({ onImport, onClose }) {
|
|
|
67365
67803
|
setLoading(false);
|
|
67366
67804
|
}
|
|
67367
67805
|
}
|
|
67806
|
+
function importCurl() {
|
|
67807
|
+
const text = curlText.trim();
|
|
67808
|
+
if (!text) return;
|
|
67809
|
+
setError(null);
|
|
67810
|
+
try {
|
|
67811
|
+
const parsed = parseCurl(text);
|
|
67812
|
+
const reqId = v4();
|
|
67813
|
+
const col = {
|
|
67814
|
+
version: "1.0",
|
|
67815
|
+
id: v4(),
|
|
67816
|
+
name: parsed.name || "Imported request",
|
|
67817
|
+
description: "",
|
|
67818
|
+
rootFolder: { id: v4(), name: "root", description: "", folders: [], requestIds: [reqId] },
|
|
67819
|
+
requests: {
|
|
67820
|
+
[reqId]: {
|
|
67821
|
+
id: reqId,
|
|
67822
|
+
name: parsed.name,
|
|
67823
|
+
method: parsed.method,
|
|
67824
|
+
url: parsed.url,
|
|
67825
|
+
headers: parsed.headers,
|
|
67826
|
+
params: parsed.params,
|
|
67827
|
+
auth: parsed.auth,
|
|
67828
|
+
body: parsed.body
|
|
67829
|
+
}
|
|
67830
|
+
}
|
|
67831
|
+
};
|
|
67832
|
+
enterPreview(col);
|
|
67833
|
+
} catch (err) {
|
|
67834
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
67835
|
+
}
|
|
67836
|
+
}
|
|
67368
67837
|
function toggleId(id2) {
|
|
67369
67838
|
setChosenIds((prev) => {
|
|
67370
67839
|
const next = new Set(prev);
|
|
@@ -67499,7 +67968,7 @@ function ImportModal({ onImport, onClose }) {
|
|
|
67499
67968
|
children: [
|
|
67500
67969
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
67501
67970
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "text-sm font-semibold text-surface-100", children: [
|
|
67502
|
-
"Import
|
|
67971
|
+
"Import - ",
|
|
67503
67972
|
previewCol.name
|
|
67504
67973
|
] }),
|
|
67505
67974
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -67751,6 +68220,24 @@ function ImportModal({ onImport, onClose }) {
|
|
|
67751
68220
|
)
|
|
67752
68221
|
] })
|
|
67753
68222
|
] }),
|
|
68223
|
+
selected === "curl" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
68224
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-surface-500 uppercase tracking-wider font-medium", children: "Paste a curl command" }),
|
|
68225
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
68226
|
+
"textarea",
|
|
68227
|
+
{
|
|
68228
|
+
value: curlText,
|
|
68229
|
+
onChange: (e) => {
|
|
68230
|
+
setCurlText(e.target.value);
|
|
68231
|
+
setError(null);
|
|
68232
|
+
},
|
|
68233
|
+
placeholder: `curl -X POST https://api.example.com/users \\
|
|
68234
|
+
-H 'Content-Type: application/json' \\
|
|
68235
|
+
-d '{"name":"Ada"}'`,
|
|
68236
|
+
rows: 6,
|
|
68237
|
+
className: "w-full text-xs bg-surface-800 border border-surface-700 rounded px-2.5 py-1.5 focus:outline-none focus:border-blue-500 placeholder-surface-600 font-mono resize-y"
|
|
68238
|
+
}
|
|
68239
|
+
)
|
|
68240
|
+
] }),
|
|
67754
68241
|
error2 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] text-red-400", children: error2 }),
|
|
67755
68242
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-2 pt-1", children: [
|
|
67756
68243
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -67761,7 +68248,15 @@ function ImportModal({ onImport, onClose }) {
|
|
|
67761
68248
|
children: "Cancel"
|
|
67762
68249
|
}
|
|
67763
68250
|
),
|
|
67764
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
68251
|
+
selected === "curl" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
68252
|
+
"button",
|
|
68253
|
+
{
|
|
68254
|
+
disabled: !curlText.trim() || loading,
|
|
68255
|
+
onClick: importCurl,
|
|
68256
|
+
className: "px-3 py-1.5 text-xs bg-blue-700 hover:bg-blue-600 disabled:bg-surface-800 disabled:text-surface-600 rounded transition-colors",
|
|
68257
|
+
children: "Parse"
|
|
68258
|
+
}
|
|
68259
|
+
) : /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
67765
68260
|
"button",
|
|
67766
68261
|
{
|
|
67767
68262
|
disabled: !selected || loading,
|
|
@@ -72085,7 +72580,7 @@ function App() {
|
|
|
72085
72580
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#6aa3c8" }, children: "Spector" }),
|
|
72086
72581
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ml-2 text-[10px] font-normal opacity-50", children: [
|
|
72087
72582
|
"v",
|
|
72088
|
-
"0.
|
|
72583
|
+
"0.4.1"
|
|
72089
72584
|
] }),
|
|
72090
72585
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mx-2 opacity-30", children: "·" }),
|
|
72091
72586
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|