@testsmith/api-spector 0.3.6 → 0.3.8
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-BtRMtQJg.js → handle-BCnNIZZr.js} +3 -1
- package/out/main/chunks/{import-CZxL6J96.js → import-DUKaMYGO.js} +2 -2
- package/out/main/chunks/{request-exec-DbNHbA8x.js → request-exec-AQD6cEgT.js} +1 -1
- package/out/main/chunks/{snapshots-rV5tPwAd.js → snapshots-NGkGQNZ4.js} +1 -1
- package/out/main/chunks/{soap-handler-CqXVreNa.js → soap-handler-h0dWIjOK.js} +2 -2
- package/out/main/contract.js +3 -3
- package/out/main/index.js +21 -4
- package/out/main/runner.js +3 -3
- package/out/main/wsdl.js +3 -3
- package/out/preload/index.js +5 -1
- package/out/renderer/assets/index-B6kFQEbI.css +2 -0
- package/out/renderer/assets/{index-htBErIFG.js → index-BK58GuOI.js} +563 -77
- package/out/renderer/index.html +2 -2
- package/package.json +1 -1
- package/out/renderer/assets/index-DWiG0dQo.css +0 -2
|
@@ -13404,14 +13404,33 @@ const createWsSlice = (set2) => ({
|
|
|
13404
13404
|
})
|
|
13405
13405
|
});
|
|
13406
13406
|
const HISTORY_CAP = 200;
|
|
13407
|
-
|
|
13407
|
+
let saveTimer = null;
|
|
13408
|
+
function persistIfEnabled(get2) {
|
|
13409
|
+
if (!get2().workspace?.settings?.persistHistory) return;
|
|
13410
|
+
if (saveTimer) clearTimeout(saveTimer);
|
|
13411
|
+
saveTimer = setTimeout(() => {
|
|
13412
|
+
window.electron.saveHistory(get2().history).catch((err) => {
|
|
13413
|
+
console.warn("persistHistory: could not save history.json", err);
|
|
13414
|
+
});
|
|
13415
|
+
}, 800);
|
|
13416
|
+
}
|
|
13417
|
+
const createHistorySlice = (set2, get2) => ({
|
|
13408
13418
|
history: [],
|
|
13409
|
-
addHistoryEntry: (entry) =>
|
|
13410
|
-
s
|
|
13411
|
-
|
|
13412
|
-
|
|
13413
|
-
|
|
13414
|
-
|
|
13419
|
+
addHistoryEntry: (entry) => {
|
|
13420
|
+
set2((s) => {
|
|
13421
|
+
s.history.unshift(entry);
|
|
13422
|
+
if (s.history.length > HISTORY_CAP) s.history.length = HISTORY_CAP;
|
|
13423
|
+
});
|
|
13424
|
+
persistIfEnabled(get2);
|
|
13425
|
+
},
|
|
13426
|
+
clearHistory: () => {
|
|
13427
|
+
set2((s) => {
|
|
13428
|
+
s.history = [];
|
|
13429
|
+
});
|
|
13430
|
+
persistIfEnabled(get2);
|
|
13431
|
+
},
|
|
13432
|
+
setHistory: (entries) => set2((s) => {
|
|
13433
|
+
s.history = entries.slice(0, HISTORY_CAP);
|
|
13415
13434
|
})
|
|
13416
13435
|
});
|
|
13417
13436
|
const createRunnerSlice = (set2) => ({
|
|
@@ -13983,6 +14002,21 @@ const createEnvironmentsSlice = (set2, get2) => ({
|
|
|
13983
14002
|
updateEnvironment: (id2, data) => set2((s) => {
|
|
13984
14003
|
if (s.environments[id2]) s.environments[id2].data = data;
|
|
13985
14004
|
}),
|
|
14005
|
+
upsertEnvVar: (envId, key, value) => {
|
|
14006
|
+
set2((s) => {
|
|
14007
|
+
const env = s.environments[envId]?.data;
|
|
14008
|
+
if (!env) return;
|
|
14009
|
+
const existing = env.variables.find((v) => v.key === key && !v.secret);
|
|
14010
|
+
if (existing) existing.value = value;
|
|
14011
|
+
else env.variables.push({ key, value, enabled: true });
|
|
14012
|
+
});
|
|
14013
|
+
const entry = get2().environments[envId];
|
|
14014
|
+
if (entry) {
|
|
14015
|
+
window.electron.saveEnvironment(entry.relPath, entry.data).catch((err) => {
|
|
14016
|
+
console.warn("upsertEnvVar: could not save environment", entry.relPath, err);
|
|
14017
|
+
});
|
|
14018
|
+
}
|
|
14019
|
+
},
|
|
13986
14020
|
addEnvironment: () => set2((s) => {
|
|
13987
14021
|
const existingNames = Object.values(s.environments).map((e) => e.data.name);
|
|
13988
14022
|
const envName = uniqueName("New Environment", existingNames);
|
|
@@ -14152,7 +14186,7 @@ const useStore = create()(
|
|
|
14152
14186
|
immer((set2, get2, api) => ({
|
|
14153
14187
|
// ── Slice composition ─────────────────────────────────────────────────────
|
|
14154
14188
|
...createWsSlice(set2),
|
|
14155
|
-
...createHistorySlice(set2),
|
|
14189
|
+
...createHistorySlice(set2, get2),
|
|
14156
14190
|
...createRunnerSlice(set2),
|
|
14157
14191
|
...createRecorderSlice(set2),
|
|
14158
14192
|
...createContractSlice(set2),
|
|
@@ -14244,7 +14278,7 @@ const useStore = create()(
|
|
|
14244
14278
|
})
|
|
14245
14279
|
}))
|
|
14246
14280
|
);
|
|
14247
|
-
const { electron: electron$
|
|
14281
|
+
const { electron: electron$s } = window;
|
|
14248
14282
|
function useAutoSave() {
|
|
14249
14283
|
const collections = useStore((s) => s.collections);
|
|
14250
14284
|
useStore((s) => s.environments);
|
|
@@ -14260,7 +14294,7 @@ function useAutoSave() {
|
|
|
14260
14294
|
for (const { relPath, data, dirty } of dirtyCollections) {
|
|
14261
14295
|
if (!dirty) continue;
|
|
14262
14296
|
try {
|
|
14263
|
-
await electron$
|
|
14297
|
+
await electron$s.saveCollection(relPath, data);
|
|
14264
14298
|
markCollectionClean(data.id);
|
|
14265
14299
|
} catch (e) {
|
|
14266
14300
|
console.error("Auto-save failed for", relPath, e);
|
|
@@ -14276,7 +14310,7 @@ function useAutoSave() {
|
|
|
14276
14310
|
if (wsTimerRef.current) clearTimeout(wsTimerRef.current);
|
|
14277
14311
|
wsTimerRef.current = setTimeout(async () => {
|
|
14278
14312
|
try {
|
|
14279
|
-
await electron$
|
|
14313
|
+
await electron$s.saveWorkspace(workspace);
|
|
14280
14314
|
} catch {
|
|
14281
14315
|
}
|
|
14282
14316
|
}, 300);
|
|
@@ -14285,7 +14319,7 @@ function useAutoSave() {
|
|
|
14285
14319
|
};
|
|
14286
14320
|
}, [workspace]);
|
|
14287
14321
|
}
|
|
14288
|
-
const { electron: electron$
|
|
14322
|
+
const { electron: electron$r } = window;
|
|
14289
14323
|
function useWorkspaceLoader() {
|
|
14290
14324
|
const loadCollection = useStore((s) => s.loadCollection);
|
|
14291
14325
|
const loadEnvironment = useStore((s) => s.loadEnvironment);
|
|
@@ -14307,16 +14341,25 @@ function useWorkspaceLoader() {
|
|
|
14307
14341
|
});
|
|
14308
14342
|
if (ws2.settings?.theme) setTheme(ws2.settings.theme);
|
|
14309
14343
|
if (typeof ws2.settings?.zoom === "number") setZoom(ws2.settings.zoom);
|
|
14344
|
+
if (ws2.settings?.persistHistory) {
|
|
14345
|
+
try {
|
|
14346
|
+
const entries = await electron$r.loadHistory();
|
|
14347
|
+
useStore.getState().setHistory(entries);
|
|
14348
|
+
} catch {
|
|
14349
|
+
}
|
|
14350
|
+
} else {
|
|
14351
|
+
useStore.getState().setHistory([]);
|
|
14352
|
+
}
|
|
14310
14353
|
for (const colPath of ws2.collections) {
|
|
14311
14354
|
try {
|
|
14312
|
-
const col = await electron$
|
|
14355
|
+
const col = await electron$r.loadCollection(colPath);
|
|
14313
14356
|
loadCollection(colPath, col);
|
|
14314
14357
|
} catch {
|
|
14315
14358
|
}
|
|
14316
14359
|
}
|
|
14317
14360
|
for (const envPath of ws2.environments) {
|
|
14318
14361
|
try {
|
|
14319
|
-
const env = await electron$
|
|
14362
|
+
const env = await electron$r.loadEnvironment(envPath);
|
|
14320
14363
|
loadEnvironment(envPath, env);
|
|
14321
14364
|
} catch {
|
|
14322
14365
|
}
|
|
@@ -14335,19 +14378,19 @@ function useWorkspaceLoader() {
|
|
|
14335
14378
|
}
|
|
14336
14379
|
for (const relPath of ws2.mocks ?? []) {
|
|
14337
14380
|
try {
|
|
14338
|
-
const mockData = await electron$
|
|
14381
|
+
const mockData = await electron$r.loadMock(relPath);
|
|
14339
14382
|
loadMock(relPath, mockData);
|
|
14340
14383
|
} catch {
|
|
14341
14384
|
}
|
|
14342
14385
|
}
|
|
14343
14386
|
try {
|
|
14344
|
-
const snapshots = await electron$
|
|
14387
|
+
const snapshots = await electron$r.listContractSnapshots(ws2.contracts ?? []);
|
|
14345
14388
|
for (const { relPath, snapshot } of snapshots) loadContractSnapshot(relPath, snapshot);
|
|
14346
14389
|
} catch {
|
|
14347
14390
|
}
|
|
14348
14391
|
if (ws2.collections.length > 0) {
|
|
14349
14392
|
try {
|
|
14350
|
-
const firstCol = await electron$
|
|
14393
|
+
const firstCol = await electron$r.loadCollection(ws2.collections[0]);
|
|
14351
14394
|
setActiveCollection(firstCol.id);
|
|
14352
14395
|
} catch {
|
|
14353
14396
|
}
|
|
@@ -33137,8 +33180,8 @@ class CompletionTooltip {
|
|
|
33137
33180
|
if (typeof section != "string" && section.header) {
|
|
33138
33181
|
ul.appendChild(section.header(section));
|
|
33139
33182
|
} else {
|
|
33140
|
-
let
|
|
33141
|
-
|
|
33183
|
+
let header2 = ul.appendChild(document.createElement("completion-section"));
|
|
33184
|
+
header2.textContent = name2;
|
|
33142
33185
|
}
|
|
33143
33186
|
}
|
|
33144
33187
|
}
|
|
@@ -35948,7 +35991,7 @@ function CollectionSettingsModal({ collection, onClose }) {
|
|
|
35948
35991
|
}
|
|
35949
35992
|
);
|
|
35950
35993
|
}
|
|
35951
|
-
const { electron: electron$
|
|
35994
|
+
const { electron: electron$q } = window;
|
|
35952
35995
|
function normalisePath(url) {
|
|
35953
35996
|
let path = url.replace(/^\{\{[^}]+\}\}/, "").replace(/^https?:\/\/[^/]+/, "");
|
|
35954
35997
|
if (!path.startsWith("/")) path = "/" + path;
|
|
@@ -36035,7 +36078,7 @@ function SchemaSyncModal({
|
|
|
36035
36078
|
setLoading(true);
|
|
36036
36079
|
setError(null);
|
|
36037
36080
|
try {
|
|
36038
|
-
const entries = await electron$
|
|
36081
|
+
const entries = await electron$q.extractOpenApiSchemas();
|
|
36039
36082
|
if (!entries) {
|
|
36040
36083
|
setLoading(false);
|
|
36041
36084
|
return;
|
|
@@ -36054,7 +36097,7 @@ function SchemaSyncModal({
|
|
|
36054
36097
|
setLoading(true);
|
|
36055
36098
|
setError(null);
|
|
36056
36099
|
try {
|
|
36057
|
-
const entries = await electron$
|
|
36100
|
+
const entries = await electron$q.extractOpenApiSchemasFromUrl(trimmed);
|
|
36058
36101
|
setSpecEntries(entries);
|
|
36059
36102
|
autoSelectChanged(entries);
|
|
36060
36103
|
} catch (err) {
|
|
@@ -36087,7 +36130,7 @@ function SchemaSyncModal({
|
|
|
36087
36130
|
}
|
|
36088
36131
|
const entry = useStore.getState().collections[collectionId];
|
|
36089
36132
|
if (entry) {
|
|
36090
|
-
await electron$
|
|
36133
|
+
await electron$q.saveCollection(entry.relPath, entry.data);
|
|
36091
36134
|
markCollectionClean(collectionId);
|
|
36092
36135
|
}
|
|
36093
36136
|
onClose();
|
|
@@ -55343,7 +55386,7 @@ function withContentType(headers, value) {
|
|
|
55343
55386
|
if (idx === -1) return [...headers, next];
|
|
55344
55387
|
return headers.map((h, i) => i === idx ? { ...h, value, enabled: true } : h);
|
|
55345
55388
|
}
|
|
55346
|
-
const { electron: electron$
|
|
55389
|
+
const { electron: electron$p } = window;
|
|
55347
55390
|
function ParamTree({ params, depth = 0 }) {
|
|
55348
55391
|
if (params.length === 0) {
|
|
55349
55392
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-surface-600 italic", children: "No parameters declared in WSDL." });
|
|
@@ -55369,7 +55412,7 @@ function SoapEditor({ request, onChange }) {
|
|
|
55369
55412
|
let cancelled = false;
|
|
55370
55413
|
(async () => {
|
|
55371
55414
|
try {
|
|
55372
|
-
const result = await electron$
|
|
55415
|
+
const result = await electron$p.wsdlFetch(url);
|
|
55373
55416
|
if (cancelled) return;
|
|
55374
55417
|
setOperations(result.operations);
|
|
55375
55418
|
setEndpoints(result.endpoints);
|
|
@@ -55406,7 +55449,7 @@ function SoapEditor({ request, onChange }) {
|
|
|
55406
55449
|
setFetching(true);
|
|
55407
55450
|
setFetchError(null);
|
|
55408
55451
|
try {
|
|
55409
|
-
const result = await electron$
|
|
55452
|
+
const result = await electron$p.wsdlFetch(soap.wsdlUrl.trim());
|
|
55410
55453
|
setOperations(result.operations);
|
|
55411
55454
|
setEndpoints(result.endpoints);
|
|
55412
55455
|
setTargetNs(result.targetNamespace);
|
|
@@ -55676,7 +55719,7 @@ function BodyTab({ request, onChange }) {
|
|
|
55676
55719
|
mode === "soap" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SoapEditor, { request, onChange }) })
|
|
55677
55720
|
] });
|
|
55678
55721
|
}
|
|
55679
|
-
const { electron: electron$
|
|
55722
|
+
const { electron: electron$o } = window;
|
|
55680
55723
|
const AUTH_TYPES = ["none", "bearer", "basic", "digest", "ntlm", "apikey", "oauth2"];
|
|
55681
55724
|
function AuthTab({ request, onChange }) {
|
|
55682
55725
|
const auth = request.auth;
|
|
@@ -55690,7 +55733,7 @@ function AuthTab({ request, onChange }) {
|
|
|
55690
55733
|
}
|
|
55691
55734
|
async function saveSecret(ref2) {
|
|
55692
55735
|
if (!secretValue || !ref2) return;
|
|
55693
|
-
await electron$
|
|
55736
|
+
await electron$o.setSecret(ref2, secretValue);
|
|
55694
55737
|
setSaved(true);
|
|
55695
55738
|
setSecretValue("");
|
|
55696
55739
|
setTimeout(() => setSaved(false), 2e3);
|
|
@@ -55702,7 +55745,7 @@ function AuthTab({ request, onChange }) {
|
|
|
55702
55745
|
setOauth2Error("");
|
|
55703
55746
|
try {
|
|
55704
55747
|
const vars = {};
|
|
55705
|
-
const result = await electron$
|
|
55748
|
+
const result = await electron$o.oauth2StartFlow(oauth2Auth, vars);
|
|
55706
55749
|
setAuth({
|
|
55707
55750
|
oauth2CachedToken: result.accessToken,
|
|
55708
55751
|
oauth2TokenExpiry: result.expiresAt
|
|
@@ -55720,7 +55763,7 @@ function AuthTab({ request, onChange }) {
|
|
|
55720
55763
|
setOauth2Status("fetching");
|
|
55721
55764
|
setOauth2Error("");
|
|
55722
55765
|
try {
|
|
55723
|
-
const result = await electron$
|
|
55766
|
+
const result = await electron$o.oauth2RefreshToken(oauth2Auth, {}, oauth2RefreshToken);
|
|
55724
55767
|
setAuth({
|
|
55725
55768
|
oauth2CachedToken: result.accessToken,
|
|
55726
55769
|
oauth2TokenExpiry: result.expiresAt
|
|
@@ -63028,7 +63071,7 @@ function SchemaTab({ request, onChange }) {
|
|
|
63028
63071
|
] }) })
|
|
63029
63072
|
] });
|
|
63030
63073
|
}
|
|
63031
|
-
const { electron: electron$
|
|
63074
|
+
const { electron: electron$n } = window;
|
|
63032
63075
|
const EMPTY = { statusCode: 200, headers: [], bodySchema: "" };
|
|
63033
63076
|
function ContractTab({ request, onChange }) {
|
|
63034
63077
|
const activeTab = useStore((s) => s.tabs.find((t2) => t2.id === s.activeTabId));
|
|
@@ -63042,7 +63085,7 @@ function ContractTab({ request, onChange }) {
|
|
|
63042
63085
|
if (!lastResponse?.body) return;
|
|
63043
63086
|
setInferring(true);
|
|
63044
63087
|
try {
|
|
63045
|
-
const schema = await electron$
|
|
63088
|
+
const schema = await electron$n.inferContractSchema(lastResponse.body);
|
|
63046
63089
|
if (schema) update({ bodySchema: schema });
|
|
63047
63090
|
} finally {
|
|
63048
63091
|
setInferring(false);
|
|
@@ -63168,7 +63211,7 @@ function ContractTab({ request, onChange }) {
|
|
|
63168
63211
|
] })
|
|
63169
63212
|
] });
|
|
63170
63213
|
}
|
|
63171
|
-
const { electron: electron$
|
|
63214
|
+
const { electron: electron$m } = window;
|
|
63172
63215
|
function formatTime$1(ts) {
|
|
63173
63216
|
const d = new Date(ts);
|
|
63174
63217
|
const hh = String(d.getHours()).padStart(2, "0");
|
|
@@ -63188,14 +63231,14 @@ function WebSocketPanel({ request }) {
|
|
|
63188
63231
|
const [sendText, setSendText] = reactExports.useState("");
|
|
63189
63232
|
const logEndRef = reactExports.useRef(null);
|
|
63190
63233
|
reactExports.useEffect(() => {
|
|
63191
|
-
electron$
|
|
63234
|
+
electron$m.onWsMessage(({ requestId, message }) => {
|
|
63192
63235
|
addWsMessage(requestId, message);
|
|
63193
63236
|
});
|
|
63194
|
-
electron$
|
|
63237
|
+
electron$m.onWsStatus(({ requestId, status, error: error2 }) => {
|
|
63195
63238
|
setWsStatus(requestId, status, error2);
|
|
63196
63239
|
});
|
|
63197
63240
|
return () => {
|
|
63198
|
-
electron$
|
|
63241
|
+
electron$m.offWsEvents();
|
|
63199
63242
|
};
|
|
63200
63243
|
}, [addWsMessage, setWsStatus]);
|
|
63201
63244
|
reactExports.useEffect(() => {
|
|
@@ -63208,19 +63251,19 @@ function WebSocketPanel({ request }) {
|
|
|
63208
63251
|
if (h.enabled && h.key) headers[h.key] = h.value;
|
|
63209
63252
|
}
|
|
63210
63253
|
try {
|
|
63211
|
-
await electron$
|
|
63254
|
+
await electron$m.wsConnect(request.id, request.url, headers);
|
|
63212
63255
|
} catch (err) {
|
|
63213
63256
|
setWsStatus(request.id, "error", err instanceof Error ? err.message : String(err));
|
|
63214
63257
|
}
|
|
63215
63258
|
}
|
|
63216
63259
|
async function disconnect() {
|
|
63217
|
-
await electron$
|
|
63260
|
+
await electron$m.wsDisconnect(request.id);
|
|
63218
63261
|
}
|
|
63219
63262
|
async function sendMessage() {
|
|
63220
63263
|
const text = sendText.trim();
|
|
63221
63264
|
if (!text || !isConnected) return;
|
|
63222
63265
|
try {
|
|
63223
|
-
await electron$
|
|
63266
|
+
await electron$m.wsSend(request.id, text);
|
|
63224
63267
|
const msg = {
|
|
63225
63268
|
id: crypto.randomUUID(),
|
|
63226
63269
|
direction: "sent",
|
|
@@ -63592,7 +63635,7 @@ function FuzzResultsPanel({ report, onClear }) {
|
|
|
63592
63635
|
] }) })
|
|
63593
63636
|
] });
|
|
63594
63637
|
}
|
|
63595
|
-
const { electron: electron$
|
|
63638
|
+
const { electron: electron$l } = window;
|
|
63596
63639
|
const WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
63597
63640
|
function FuzzModal({ request, onClose }) {
|
|
63598
63641
|
const environments = useStore((s) => s.environments);
|
|
@@ -63618,7 +63661,7 @@ function FuzzModal({ request, onClose }) {
|
|
|
63618
63661
|
const env = resolveEnvironmentById(environments, activeEnvironmentId);
|
|
63619
63662
|
const envVars = env ? Object.fromEntries(env.variables.filter((v) => v.enabled).map((v) => [v.key, v.value])) : {};
|
|
63620
63663
|
const collectionVars = activeCollectionId ? collections[activeCollectionId]?.data.collectionVariables ?? {} : {};
|
|
63621
|
-
const result = await electron$
|
|
63664
|
+
const result = await electron$l.fuzzContracts({
|
|
63622
63665
|
requests: [request],
|
|
63623
63666
|
envVars,
|
|
63624
63667
|
collectionVars,
|
|
@@ -63710,7 +63753,7 @@ function FuzzModal({ request, onClose }) {
|
|
|
63710
63753
|
}
|
|
63711
63754
|
);
|
|
63712
63755
|
}
|
|
63713
|
-
const { electron: electron$
|
|
63756
|
+
const { electron: electron$k } = window;
|
|
63714
63757
|
const METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "QUERY"];
|
|
63715
63758
|
const METHOD_COLORS = {
|
|
63716
63759
|
GET: "text-emerald-400",
|
|
@@ -63802,7 +63845,7 @@ function RequestBuilder({ request }) {
|
|
|
63802
63845
|
activeEnvironmentId
|
|
63803
63846
|
);
|
|
63804
63847
|
const hookSessionVars = useStore.getState().sessionVars;
|
|
63805
|
-
const r = await electron$
|
|
63848
|
+
const r = await electron$k.sendRequest({
|
|
63806
63849
|
...basePayload,
|
|
63807
63850
|
environment: hookEnv,
|
|
63808
63851
|
request: hook,
|
|
@@ -63846,7 +63889,7 @@ function RequestBuilder({ request }) {
|
|
|
63846
63889
|
activeEnvironmentId
|
|
63847
63890
|
);
|
|
63848
63891
|
const freshSessionVars = useStore.getState().sessionVars;
|
|
63849
|
-
const result = await electron$
|
|
63892
|
+
const result = await electron$k.sendRequest({
|
|
63850
63893
|
...basePayload,
|
|
63851
63894
|
environment: freshEnv,
|
|
63852
63895
|
request: mergedRequest,
|
|
@@ -63874,7 +63917,7 @@ function RequestBuilder({ request }) {
|
|
|
63874
63917
|
activeEnvironmentId
|
|
63875
63918
|
);
|
|
63876
63919
|
const hookSessionVars = useStore.getState().sessionVars;
|
|
63877
|
-
const r = await electron$
|
|
63920
|
+
const r = await electron$k.sendRequest({
|
|
63878
63921
|
...basePayload,
|
|
63879
63922
|
environment: hookEnv,
|
|
63880
63923
|
request: hook,
|
|
@@ -64650,7 +64693,7 @@ function HookResultsPanel({ results }) {
|
|
|
64650
64693
|
}) })
|
|
64651
64694
|
] });
|
|
64652
64695
|
}
|
|
64653
|
-
const { electron: electron$
|
|
64696
|
+
const { electron: electron$j } = window;
|
|
64654
64697
|
function extractPath(url) {
|
|
64655
64698
|
try {
|
|
64656
64699
|
return new URL(url).pathname || "/";
|
|
@@ -64704,14 +64747,14 @@ function SaveAsMockModal({ onClose }) {
|
|
|
64704
64747
|
const entry = state.mocks[serverId];
|
|
64705
64748
|
const updated = { ...entry.data, name: newServerName, port: Number(newServerPort), routes: [route] };
|
|
64706
64749
|
updateMock(serverId, updated);
|
|
64707
|
-
await electron$
|
|
64750
|
+
await electron$j.saveMock(entry.relPath, updated);
|
|
64708
64751
|
const ws2 = useStore.getState().workspace;
|
|
64709
|
-
if (ws2) await electron$
|
|
64752
|
+
if (ws2) await electron$j.saveWorkspace(ws2);
|
|
64710
64753
|
} else {
|
|
64711
64754
|
const entry = useStore.getState().mocks[serverId];
|
|
64712
64755
|
const updated = { ...entry.data, routes: [...entry.data.routes, route] };
|
|
64713
64756
|
updateMock(serverId, updated);
|
|
64714
|
-
await electron$
|
|
64757
|
+
await electron$j.saveMock(entry.relPath, updated);
|
|
64715
64758
|
}
|
|
64716
64759
|
onClose();
|
|
64717
64760
|
} finally {
|
|
@@ -64994,7 +65037,7 @@ function RequestPanel({ sentRequest }) {
|
|
|
64994
65037
|
if (!sentRequest) {
|
|
64995
65038
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center h-full text-surface-400 text-xs", children: "Send a request to see what was transmitted." });
|
|
64996
65039
|
}
|
|
64997
|
-
const
|
|
65040
|
+
const hasBody2 = sentRequest.body !== void 0 && sentRequest.body !== "";
|
|
64998
65041
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-h-0 overflow-y-auto text-xs font-mono", children: [
|
|
64999
65042
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-3 border-b border-surface-800 flex items-center gap-3", children: [
|
|
65000
65043
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-blue-400 shrink-0", children: sentRequest.method }),
|
|
@@ -65007,7 +65050,7 @@ function RequestPanel({ sentRequest }) {
|
|
|
65007
65050
|
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-1 text-white break-all", children: v })
|
|
65008
65051
|
] }, k)) }) })
|
|
65009
65052
|
] }),
|
|
65010
|
-
|
|
65053
|
+
hasBody2 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-2", children: [
|
|
65011
65054
|
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-surface-400 uppercase tracking-wider font-medium mb-1.5", children: "Body" }),
|
|
65012
65055
|
/* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "text-white whitespace-pre-wrap break-all text-[11px]", children: sentRequest.body })
|
|
65013
65056
|
] })
|
|
@@ -65043,7 +65086,183 @@ function ConsolePanel({ scriptResult }) {
|
|
|
65043
65086
|
)) })
|
|
65044
65087
|
] });
|
|
65045
65088
|
}
|
|
65046
|
-
|
|
65089
|
+
function header(headers, name2) {
|
|
65090
|
+
const lower = name2.toLowerCase();
|
|
65091
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
65092
|
+
if (k.toLowerCase() === lower) return v;
|
|
65093
|
+
}
|
|
65094
|
+
return void 0;
|
|
65095
|
+
}
|
|
65096
|
+
function hasBody(res) {
|
|
65097
|
+
return (res.bodySize ?? res.body.length) > 0;
|
|
65098
|
+
}
|
|
65099
|
+
const REDIRECTS_NEEDING_LOCATION = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
|
|
65100
|
+
const BODILESS = {
|
|
65101
|
+
204: { label: "No Content", ref: "RFC 9110 §15.3.5" },
|
|
65102
|
+
205: { label: "Reset Content", ref: "RFC 9110 §15.3.6" },
|
|
65103
|
+
304: { label: "Not Modified", ref: "RFC 9110 §15.4.5" }
|
|
65104
|
+
};
|
|
65105
|
+
function validateHttpSemantics(res, opts = {}) {
|
|
65106
|
+
if (!res.status || res.status === 0) return [];
|
|
65107
|
+
const f = [];
|
|
65108
|
+
const method = res.method.toUpperCase();
|
|
65109
|
+
const { status } = res;
|
|
65110
|
+
const body = hasBody(res);
|
|
65111
|
+
const contentType = header(res.headers, "content-type");
|
|
65112
|
+
const contentEncoding = header(res.headers, "content-encoding");
|
|
65113
|
+
const clNum = Number(header(res.headers, "content-length") ?? NaN);
|
|
65114
|
+
const isBodiless = status in BODILESS || status >= 100 && status < 200;
|
|
65115
|
+
if (isBodiless) {
|
|
65116
|
+
const declaresBody = body || Number.isFinite(clNum) && clNum > 0;
|
|
65117
|
+
if (declaresBody) {
|
|
65118
|
+
const info = BODILESS[status] ?? { label: "Informational", ref: "RFC 9110 §15.2" };
|
|
65119
|
+
const via = body ? "" : ` (it declares Content-Length: ${clNum}; the body was discarded by the client)`;
|
|
65120
|
+
f.push({
|
|
65121
|
+
rule: status in BODILESS ? `no-body-${status}` : "no-body-1xx",
|
|
65122
|
+
severity: "error",
|
|
65123
|
+
message: `${status} ${info.label} must not include a message body${via}.`,
|
|
65124
|
+
ref: info.ref
|
|
65125
|
+
});
|
|
65126
|
+
}
|
|
65127
|
+
}
|
|
65128
|
+
if (method === "HEAD" && body) {
|
|
65129
|
+
f.push({ rule: "no-body-head", severity: "error", message: "Response to a HEAD request must not include a body.", ref: "RFC 9110 §9.3.2" });
|
|
65130
|
+
}
|
|
65131
|
+
if (status === 206 && !header(res.headers, "content-range")) {
|
|
65132
|
+
f.push({ rule: "206-no-content-range", severity: "error", message: "206 Partial Content must include a Content-Range header.", ref: "RFC 9110 §15.3.7" });
|
|
65133
|
+
}
|
|
65134
|
+
if (status === 416 && !header(res.headers, "content-range")) {
|
|
65135
|
+
f.push({ rule: "416-no-content-range", severity: "warning", message: '416 Range Not Satisfiable should include a Content-Range header (e.g. "bytes */1234").', ref: "RFC 9110 §15.5.17" });
|
|
65136
|
+
}
|
|
65137
|
+
if (REDIRECTS_NEEDING_LOCATION.has(status) && !header(res.headers, "location")) {
|
|
65138
|
+
f.push({ rule: "redirect-no-location", severity: "error", message: `${status} redirect has no Location header, so the client cannot follow it.`, ref: "RFC 9110 §15.4" });
|
|
65139
|
+
}
|
|
65140
|
+
if (status === 401 && !header(res.headers, "www-authenticate")) {
|
|
65141
|
+
f.push({ rule: "401-no-www-authenticate", severity: "error", message: "401 Unauthorized must include a WWW-Authenticate header.", ref: "RFC 9110 §15.5.2" });
|
|
65142
|
+
}
|
|
65143
|
+
if (status === 405 && !header(res.headers, "allow")) {
|
|
65144
|
+
f.push({ rule: "405-no-allow", severity: "error", message: "405 Method Not Allowed must include an Allow header listing valid methods.", ref: "RFC 9110 §15.5.6" });
|
|
65145
|
+
}
|
|
65146
|
+
if (body && !contentType && status !== 204 && status !== 304) {
|
|
65147
|
+
f.push({ rule: "body-no-content-type", severity: "warning", message: "Response has a body but no Content-Type header; clients must guess how to parse it.", ref: "RFC 9110 §8.3" });
|
|
65148
|
+
}
|
|
65149
|
+
if (body && contentType && /application\/(json|.*\+json)/i.test(contentType)) {
|
|
65150
|
+
try {
|
|
65151
|
+
JSON.parse(res.body);
|
|
65152
|
+
} catch {
|
|
65153
|
+
f.push({ rule: "json-invalid", severity: "error", message: `Content-Type is "${contentType}" but the body is not valid JSON.`, ref: "RFC 8259" });
|
|
65154
|
+
}
|
|
65155
|
+
}
|
|
65156
|
+
if (body && contentType && /[/+]xml\b/i.test(contentType) && opts.checkXml?.(res.body) === false) {
|
|
65157
|
+
f.push({ rule: "xml-invalid", severity: "error", message: `Content-Type is "${contentType}" but the body is not well-formed XML.`, ref: "XML 1.0 §2.1" });
|
|
65158
|
+
}
|
|
65159
|
+
if (contentType && /^text\//i.test(contentType) && !/charset=/i.test(contentType)) {
|
|
65160
|
+
f.push({ rule: "text-no-charset", severity: "hint", message: `"${contentType}" has no charset parameter; clients may misinterpret the encoding.`, ref: "RFC 9110 §8.3.2" });
|
|
65161
|
+
}
|
|
65162
|
+
const clRaw = header(res.headers, "content-length");
|
|
65163
|
+
if (clRaw !== void 0 && !contentEncoding && method !== "HEAD" && !isBodiless) {
|
|
65164
|
+
const actual = res.bodySize ?? res.body.length;
|
|
65165
|
+
if (Number.isFinite(clNum) && clNum !== actual) {
|
|
65166
|
+
f.push({ rule: "content-length-mismatch", severity: "error", message: `Content-Length is ${clNum} but the body is ${actual} bytes.`, ref: "RFC 9110 §8.6" });
|
|
65167
|
+
}
|
|
65168
|
+
}
|
|
65169
|
+
if (status < 100 || status > 599) {
|
|
65170
|
+
f.push({ rule: "status-out-of-range", severity: "warning", message: `${status} is not a valid HTTP status code (must be 100-599).`, ref: "RFC 9110 §15" });
|
|
65171
|
+
}
|
|
65172
|
+
if (!header(res.headers, "date") && status >= 200) {
|
|
65173
|
+
f.push({ rule: "no-date", severity: "hint", message: "No Date header; origin servers are expected to send one.", ref: "RFC 9110 §6.6.1" });
|
|
65174
|
+
}
|
|
65175
|
+
if ((status === 429 || status === 503) && !header(res.headers, "retry-after")) {
|
|
65176
|
+
f.push({ rule: "no-retry-after", severity: "hint", message: `${status} should include a Retry-After header telling clients when to retry.`, ref: "RFC 9110 §10.2.3" });
|
|
65177
|
+
}
|
|
65178
|
+
if (status === 201 && !header(res.headers, "location")) {
|
|
65179
|
+
f.push({ rule: "201-no-location", severity: "warning", message: "201 Created should include a Location header pointing at the new resource.", ref: "RFC 9110 §15.3.2" });
|
|
65180
|
+
}
|
|
65181
|
+
const order = { error: 0, warning: 1, hint: 2 };
|
|
65182
|
+
return f.sort((a, b) => order[a.severity] - order[b.severity]);
|
|
65183
|
+
}
|
|
65184
|
+
const { electron: electron$i } = window;
|
|
65185
|
+
function xmlWellFormed(body) {
|
|
65186
|
+
try {
|
|
65187
|
+
const doc2 = new DOMParser().parseFromString(body, "application/xml");
|
|
65188
|
+
return doc2.getElementsByTagName("parsererror").length === 0;
|
|
65189
|
+
} catch {
|
|
65190
|
+
return true;
|
|
65191
|
+
}
|
|
65192
|
+
}
|
|
65193
|
+
function requestBodyText(body) {
|
|
65194
|
+
switch (body?.mode) {
|
|
65195
|
+
case "json":
|
|
65196
|
+
return body.json ?? "";
|
|
65197
|
+
case "raw":
|
|
65198
|
+
return body.raw ?? "";
|
|
65199
|
+
case "graphql":
|
|
65200
|
+
return body.graphql?.query ?? "";
|
|
65201
|
+
case "soap":
|
|
65202
|
+
return body.soap?.envelope ?? "";
|
|
65203
|
+
case "form":
|
|
65204
|
+
return (body.form ?? []).filter((p2) => p2.enabled && p2.key).map((p2) => `${p2.key}=${p2.value}`).join("\n");
|
|
65205
|
+
default:
|
|
65206
|
+
return "";
|
|
65207
|
+
}
|
|
65208
|
+
}
|
|
65209
|
+
function KVBlock({ label, rows }) {
|
|
65210
|
+
if (rows.length === 0) return null;
|
|
65211
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
65212
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] uppercase tracking-wider text-surface-600 font-medium", children: label }),
|
|
65213
|
+
rows.map(([k, v]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 font-mono text-[10px]", children: [
|
|
65214
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-surface-500 shrink-0", children: [
|
|
65215
|
+
k,
|
|
65216
|
+
":"
|
|
65217
|
+
] }),
|
|
65218
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-300 break-all", children: v })
|
|
65219
|
+
] }, k))
|
|
65220
|
+
] });
|
|
65221
|
+
}
|
|
65222
|
+
function HistoryTabRow({ entry, onLoad }) {
|
|
65223
|
+
const [open, setOpen] = reactExports.useState(false);
|
|
65224
|
+
const reqBody = requestBodyText(entry.request.body);
|
|
65225
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-b border-surface-800", children: [
|
|
65226
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 px-4 py-2 hover:bg-surface-800/40", children: [
|
|
65227
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setOpen((o) => !o), className: "text-surface-600 text-xs w-3 shrink-0", children: open ? "▾" : "▸" }),
|
|
65228
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] font-bold font-mono shrink-0 w-10 ${getMethodColor(entry.request.method)}`, children: entry.request.method }),
|
|
65229
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-xs font-bold font-mono shrink-0 w-8 ${getStatusColor(entry.response.status)}`, children: entry.response.status || "ERR" }),
|
|
65230
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-surface-400 shrink-0", children: [
|
|
65231
|
+
entry.response.durationMs,
|
|
65232
|
+
"ms"
|
|
65233
|
+
] }),
|
|
65234
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[11px] text-surface-500 shrink-0", children: [
|
|
65235
|
+
(entry.response.bodySize / 1024).toFixed(1),
|
|
65236
|
+
" KB"
|
|
65237
|
+
] }),
|
|
65238
|
+
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 }),
|
|
65239
|
+
/* @__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" }) }),
|
|
65240
|
+
/* @__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" })
|
|
65241
|
+
] }),
|
|
65242
|
+
open && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 pb-3 pt-1 flex flex-col gap-3 bg-surface-950/40", children: [
|
|
65243
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
65244
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] uppercase tracking-wider text-surface-500 font-semibold", children: "Request" }),
|
|
65245
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "font-mono text-[10px] text-surface-300 break-all", children: [
|
|
65246
|
+
entry.request.method,
|
|
65247
|
+
" ",
|
|
65248
|
+
entry.resolvedUrl
|
|
65249
|
+
] }),
|
|
65250
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(KVBlock, { label: "Headers", rows: entry.request.headers.filter((h) => h.enabled && h.key).map((h) => [h.key, h.value]) }),
|
|
65251
|
+
reqBody && /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "text-[10px] font-mono text-surface-300 bg-surface-900 border border-surface-800 rounded px-2 py-1.5 overflow-x-auto whitespace-pre-wrap break-words max-h-40 overflow-y-auto", children: reqBody })
|
|
65252
|
+
] }),
|
|
65253
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
65254
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] uppercase tracking-wider text-surface-500 font-semibold", children: "Response" }),
|
|
65255
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "font-mono text-[10px]", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: getStatusColor(entry.response.status), children: [
|
|
65256
|
+
entry.response.status,
|
|
65257
|
+
" ",
|
|
65258
|
+
entry.response.statusText
|
|
65259
|
+
] }) }),
|
|
65260
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(KVBlock, { label: "Headers", rows: Object.entries(entry.response.headers) }),
|
|
65261
|
+
entry.response.body && /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "text-[10px] font-mono text-surface-300 bg-surface-900 border border-surface-800 rounded px-2 py-1.5 overflow-x-auto whitespace-pre-wrap break-words max-h-56 overflow-y-auto", children: entry.response.body })
|
|
65262
|
+
] })
|
|
65263
|
+
] })
|
|
65264
|
+
] });
|
|
65265
|
+
}
|
|
65047
65266
|
function ResponseViewer() {
|
|
65048
65267
|
const activeTab = useStore((s) => s.tabs.find((t2) => t2.id === s.activeTabId));
|
|
65049
65268
|
const activeTabId = useStore((s) => s.activeTabId);
|
|
@@ -65058,7 +65277,25 @@ function ResponseViewer() {
|
|
|
65058
65277
|
const sentRequest = activeTab?.lastSentRequest ?? null;
|
|
65059
65278
|
const hookResults = activeTab?.lastHookResults ?? null;
|
|
65060
65279
|
const requestId = activeTab?.requestId ?? null;
|
|
65280
|
+
const setTabResponse = useStore((s) => s.setTabResponse);
|
|
65281
|
+
const history2 = useStore((s) => s.history);
|
|
65282
|
+
const activeEnvironmentId = useStore((s) => s.activeEnvironmentId);
|
|
65283
|
+
const environments = useStore((s) => s.environments);
|
|
65284
|
+
const upsertEnvVar = useStore((s) => s.upsertEnvVar);
|
|
65061
65285
|
const [tab, setTab] = reactExports.useState("body");
|
|
65286
|
+
const requestHistory = requestId ? history2.filter((e) => e.request.id === requestId) : [];
|
|
65287
|
+
const httpFindings = response && !response.error ? validateHttpSemantics({
|
|
65288
|
+
method: sentRequest?.method ?? "GET",
|
|
65289
|
+
status: response.status,
|
|
65290
|
+
statusText: response.statusText,
|
|
65291
|
+
headers: response.headers,
|
|
65292
|
+
body: response.body,
|
|
65293
|
+
bodySize: response.bodySize
|
|
65294
|
+
}, { checkXml: xmlWellFormed }) : [];
|
|
65295
|
+
const httpErrors = httpFindings.filter((x) => x.severity === "error").length;
|
|
65296
|
+
const [headerMenu, setHeaderMenu] = reactExports.useState(null);
|
|
65297
|
+
const [varDialog, setVarDialog] = reactExports.useState(null);
|
|
65298
|
+
const activeEnvName = activeEnvironmentId ? environments[activeEnvironmentId]?.data.name : void 0;
|
|
65062
65299
|
reactExports.useEffect(() => {
|
|
65063
65300
|
if (scriptResult?.preScriptError || scriptResult?.postScriptError) {
|
|
65064
65301
|
setTab("console");
|
|
@@ -65071,7 +65308,7 @@ function ResponseViewer() {
|
|
|
65071
65308
|
const contractToast = useToast(2500);
|
|
65072
65309
|
async function saveAsContract() {
|
|
65073
65310
|
if (!response || !requestId || !activeTabId) return;
|
|
65074
|
-
const schema = response.body ? await electron$
|
|
65311
|
+
const schema = response.body ? await electron$i.inferContractSchema(response.body) : null;
|
|
65075
65312
|
const contentType2 = response.headers["content-type"];
|
|
65076
65313
|
const headers = contentType2 ? [{ key: "content-type", value: contentType2, required: true }] : [];
|
|
65077
65314
|
updateRequest(requestId, {
|
|
@@ -65115,16 +65352,26 @@ function ResponseViewer() {
|
|
|
65115
65352
|
const isXml = !isJson && (contentType.includes("xml") || contentType.includes("html"));
|
|
65116
65353
|
const supportsTree = isJson || isXml;
|
|
65117
65354
|
const displayBody = isJson ? prettyJson(response.body) : isXml ? prettyXml(response.body) : response.body;
|
|
65355
|
+
const bodyParseError = response.body.trim().length > 0 && (isJson && (() => {
|
|
65356
|
+
try {
|
|
65357
|
+
JSON.parse(response.body);
|
|
65358
|
+
return false;
|
|
65359
|
+
} catch {
|
|
65360
|
+
return true;
|
|
65361
|
+
}
|
|
65362
|
+
})() || isXml && contentType.includes("xml") && !xmlWellFormed(response.body));
|
|
65118
65363
|
const passedCount = scriptResult?.testResults.filter((t2) => t2.passed).length ?? 0;
|
|
65119
65364
|
const totalCount = scriptResult?.testResults.length ?? 0;
|
|
65120
65365
|
const consoleCount = scriptResult?.consoleOutput.length ?? 0;
|
|
65121
65366
|
const hasScriptError = !!(scriptResult?.preScriptError || scriptResult?.postScriptError);
|
|
65122
65367
|
const tabList = [
|
|
65123
65368
|
{ id: "request", label: "Request" },
|
|
65124
|
-
{ id: "body", label: "Body" },
|
|
65369
|
+
{ id: "body", label: "Body", badge: bodyParseError ? "!" : void 0, error: bodyParseError },
|
|
65125
65370
|
{ id: "headers", label: "Headers" },
|
|
65126
65371
|
{ id: "tests", label: "Tests", badge: totalCount > 0 ? `${passedCount}/${totalCount}` : void 0 },
|
|
65127
|
-
{ id: "console", label: "Console", badge: hasScriptError ? "!" : consoleCount > 0 ? consoleCount : void 0, error: hasScriptError }
|
|
65372
|
+
{ id: "console", label: "Console", badge: hasScriptError ? "!" : consoleCount > 0 ? consoleCount : void 0, error: hasScriptError },
|
|
65373
|
+
{ id: "history", label: "History", badge: requestHistory.length > 0 ? requestHistory.length : void 0 },
|
|
65374
|
+
{ id: "http", label: "HTTP", badge: httpFindings.length > 0 ? httpErrors > 0 ? "!" : httpFindings.length : void 0, error: httpErrors > 0 }
|
|
65128
65375
|
];
|
|
65129
65376
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-full flex flex-col", children: [
|
|
65130
65377
|
hookResults && hookResults.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(HookResultsPanel, { results: hookResults }),
|
|
@@ -65232,13 +65479,122 @@ function ResponseViewer() {
|
|
|
65232
65479
|
readOnly: true,
|
|
65233
65480
|
basicSetup: { lineNumbers: true, foldGutter: true }
|
|
65234
65481
|
}
|
|
65235
|
-
) : tab === "headers" ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsx("table", { className: "w-full text-xs px-4 py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { children: Object.entries(response.headers).map(([k, v]) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
65236
|
-
|
|
65237
|
-
|
|
65238
|
-
|
|
65482
|
+
) : tab === "headers" ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsx("table", { className: "w-full text-xs px-4 py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { children: Object.entries(response.headers).map(([k, v]) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
65483
|
+
"tr",
|
|
65484
|
+
{
|
|
65485
|
+
className: "border-b border-surface-800 hover:bg-surface-800/40",
|
|
65486
|
+
onContextMenu: (e) => {
|
|
65487
|
+
e.preventDefault();
|
|
65488
|
+
setHeaderMenu({ x: e.clientX, y: e.clientY, key: k, value: v });
|
|
65489
|
+
},
|
|
65490
|
+
title: "Right-click to create an environment variable",
|
|
65491
|
+
children: [
|
|
65492
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-1.5 px-4 text-surface-400 font-mono w-56 align-top", children: k }),
|
|
65493
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-1.5 px-4 text-white font-mono break-all", children: v })
|
|
65494
|
+
]
|
|
65495
|
+
},
|
|
65496
|
+
k
|
|
65497
|
+
)) }) }) }) : tab === "tests" ? /* @__PURE__ */ jsxRuntimeExports.jsx(TestsPanel, { scriptResult }) : tab === "console" ? /* @__PURE__ */ jsxRuntimeExports.jsx(ConsolePanel, { scriptResult }) : tab === "request" ? /* @__PURE__ */ jsxRuntimeExports.jsx(RequestPanel, { sentRequest }) : tab === "http" ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0 overflow-y-auto p-4", children: httpFindings.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center h-full gap-2 text-center", children: [
|
|
65498
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-2xl", children: "✓" }),
|
|
65499
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-emerald-400", children: "Conforms to HTTP semantics" }),
|
|
65500
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-surface-500 max-w-sm", children: "No violations of the HTTP specification (RFC 9110/9111) in this response. This check is automatic and needs no test or spec." })
|
|
65501
|
+
] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-col gap-2 max-w-3xl", children: httpFindings.map((find2, i) => {
|
|
65502
|
+
const tone = find2.severity === "error" ? "border-red-800/60 bg-red-950/20" : find2.severity === "warning" ? "border-amber-800/50 bg-amber-950/20" : "border-surface-700 bg-surface-800/40";
|
|
65503
|
+
const label = find2.severity === "error" ? "text-red-400" : find2.severity === "warning" ? "text-amber-400" : "text-surface-400";
|
|
65504
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `border-l-2 rounded-r px-3 py-2 ${tone}`, children: [
|
|
65505
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
|
|
65506
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] font-bold uppercase tracking-wider ${label}`, children: find2.severity }),
|
|
65507
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-mono text-surface-500 bg-surface-900 px-1.5 py-0.5 rounded", children: find2.rule }),
|
|
65508
|
+
find2.ref && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-surface-600 ml-auto", children: find2.ref })
|
|
65509
|
+
] }),
|
|
65510
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-surface-200 mt-1", children: find2.message })
|
|
65511
|
+
] }, i);
|
|
65512
|
+
}) }) }) : tab === "history" ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: 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(
|
|
65513
|
+
HistoryTabRow,
|
|
65514
|
+
{
|
|
65515
|
+
entry,
|
|
65516
|
+
onLoad: () => {
|
|
65517
|
+
if (activeTabId) setTabResponse(activeTabId, entry.response, entry.scriptResult ?? null);
|
|
65518
|
+
}
|
|
65519
|
+
},
|
|
65520
|
+
entry.id
|
|
65521
|
+
)) }) }) : null }),
|
|
65522
|
+
headerMenu && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
65523
|
+
ContextMenu,
|
|
65524
|
+
{
|
|
65525
|
+
x: headerMenu.x,
|
|
65526
|
+
y: headerMenu.y,
|
|
65527
|
+
onClose: () => setHeaderMenu(null),
|
|
65528
|
+
items: [
|
|
65529
|
+
{ type: "header", label: headerMenu.key },
|
|
65530
|
+
activeEnvironmentId ? {
|
|
65531
|
+
type: "item",
|
|
65532
|
+
label: `Create variable in "${activeEnvName}"`,
|
|
65533
|
+
onClick: () => {
|
|
65534
|
+
setVarDialog({ name: headerMenu.key.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, ""), value: headerMenu.value });
|
|
65535
|
+
setHeaderMenu(null);
|
|
65536
|
+
}
|
|
65537
|
+
} : { type: "header", label: "Select an environment first" }
|
|
65538
|
+
]
|
|
65539
|
+
}
|
|
65540
|
+
),
|
|
65541
|
+
varDialog && /* @__PURE__ */ jsxRuntimeExports.jsx(Modal, { onClose: () => setVarDialog(null), title: "Create environment variable", panelClassName: "bg-surface-900 border border-surface-800 rounded-lg shadow-2xl w-[420px]", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-3 p-4", children: [
|
|
65542
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex flex-col gap-1", children: [
|
|
65543
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] uppercase tracking-wider text-surface-600 font-medium", children: "Variable name" }),
|
|
65544
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
65545
|
+
"input",
|
|
65546
|
+
{
|
|
65547
|
+
autoFocus: true,
|
|
65548
|
+
value: varDialog.name,
|
|
65549
|
+
onChange: (e) => setVarDialog((d) => d && { ...d, name: e.target.value }),
|
|
65550
|
+
className: "bg-surface-800 border border-surface-700 rounded px-2.5 py-1.5 text-sm font-mono focus:outline-none focus:border-blue-500"
|
|
65551
|
+
}
|
|
65552
|
+
)
|
|
65553
|
+
] }),
|
|
65554
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex flex-col gap-1", children: [
|
|
65555
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] uppercase tracking-wider text-surface-600 font-medium", children: "Value" }),
|
|
65556
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
65557
|
+
"input",
|
|
65558
|
+
{
|
|
65559
|
+
value: varDialog.value,
|
|
65560
|
+
onChange: (e) => setVarDialog((d) => d && { ...d, value: e.target.value }),
|
|
65561
|
+
className: "bg-surface-800 border border-surface-700 rounded px-2.5 py-1.5 text-sm font-mono focus:outline-none focus:border-blue-500"
|
|
65562
|
+
}
|
|
65563
|
+
)
|
|
65564
|
+
] }),
|
|
65565
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[11px] text-surface-500", children: [
|
|
65566
|
+
"Saved to ",
|
|
65567
|
+
activeEnvName ? `"${activeEnvName}"` : "the active environment",
|
|
65568
|
+
". Use it as ",
|
|
65569
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("code", { className: "text-surface-300", children: [
|
|
65570
|
+
"{{",
|
|
65571
|
+
varDialog.name || "name",
|
|
65572
|
+
"}}"
|
|
65573
|
+
] }),
|
|
65574
|
+
"."
|
|
65575
|
+
] }),
|
|
65576
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-2 mt-1", children: [
|
|
65577
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setVarDialog(null), className: "px-3 py-1.5 text-xs text-surface-400 hover:text-surface-200 transition-colors", children: "Cancel" }),
|
|
65578
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
65579
|
+
"button",
|
|
65580
|
+
{
|
|
65581
|
+
onClick: () => {
|
|
65582
|
+
if (activeEnvironmentId && varDialog.name.trim()) {
|
|
65583
|
+
upsertEnvVar(activeEnvironmentId, varDialog.name.trim(), varDialog.value);
|
|
65584
|
+
assertToast.show(`✓ Saved {{${varDialog.name.trim()}}}`, true);
|
|
65585
|
+
}
|
|
65586
|
+
setVarDialog(null);
|
|
65587
|
+
},
|
|
65588
|
+
disabled: !varDialog.name.trim(),
|
|
65589
|
+
className: "px-3 py-1.5 text-xs rounded bg-blue-600 hover:bg-blue-500 text-white font-semibold disabled:opacity-50 transition-colors",
|
|
65590
|
+
children: "Create"
|
|
65591
|
+
}
|
|
65592
|
+
)
|
|
65593
|
+
] })
|
|
65594
|
+
] }) })
|
|
65239
65595
|
] });
|
|
65240
65596
|
}
|
|
65241
|
-
const { electron: electron$
|
|
65597
|
+
const { electron: electron$h } = window;
|
|
65242
65598
|
const TARGETS = [
|
|
65243
65599
|
{ id: "robot_framework", label: "Robot Framework", description: "Python RequestsLibrary keywords + test suite" },
|
|
65244
65600
|
{ id: "playwright_ts", label: "Playwright TS", description: "TypeScript page-object API classes + spec files" },
|
|
@@ -65278,7 +65634,7 @@ function GeneratorPanel() {
|
|
|
65278
65634
|
try {
|
|
65279
65635
|
const col = collections[selectedCollectionId]?.data;
|
|
65280
65636
|
const env = resolveEnvironmentById(environments, activeEnvironmentId);
|
|
65281
|
-
const generated = await electron$
|
|
65637
|
+
const generated = await electron$h.generateCode({ collection: col, environment: env, target });
|
|
65282
65638
|
setFiles(generated);
|
|
65283
65639
|
setSelectedFile(generated[0]?.path ?? null);
|
|
65284
65640
|
} catch (e) {
|
|
@@ -65290,7 +65646,7 @@ function GeneratorPanel() {
|
|
|
65290
65646
|
async function saveZip() {
|
|
65291
65647
|
if (files.length === 0) return;
|
|
65292
65648
|
const col = collections[selectedCollectionId]?.data;
|
|
65293
|
-
await electron$
|
|
65649
|
+
await electron$h.saveGeneratedFilesAsZip(files, col?.name ?? "api-tests", target);
|
|
65294
65650
|
}
|
|
65295
65651
|
const selectedContent = files.find((f) => f.path === selectedFile)?.content ?? "";
|
|
65296
65652
|
const activeTarget = TARGETS.find((t2) => t2.id === target);
|
|
@@ -65389,6 +65745,92 @@ function GeneratorPanel() {
|
|
|
65389
65745
|
files.length === 0 && !generating && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 flex items-center justify-center text-surface-400 text-xs text-center px-6", children: "Select a collection and hit Generate to preview the output code." })
|
|
65390
65746
|
] });
|
|
65391
65747
|
}
|
|
65748
|
+
function enabledHeaders(headers) {
|
|
65749
|
+
return headers.filter((h) => h.enabled && h.key).map((h) => ({ name: h.key, value: h.value }));
|
|
65750
|
+
}
|
|
65751
|
+
function queryStringOf(url) {
|
|
65752
|
+
const q = url.indexOf("?");
|
|
65753
|
+
if (q === -1) return [];
|
|
65754
|
+
const out = [];
|
|
65755
|
+
for (const pair2 of url.slice(q + 1).split("&")) {
|
|
65756
|
+
if (!pair2) continue;
|
|
65757
|
+
const eq = pair2.indexOf("=");
|
|
65758
|
+
const name2 = eq === -1 ? pair2 : pair2.slice(0, eq);
|
|
65759
|
+
const value = eq === -1 ? "" : pair2.slice(eq + 1);
|
|
65760
|
+
try {
|
|
65761
|
+
out.push({ name: decodeURIComponent(name2), value: decodeURIComponent(value) });
|
|
65762
|
+
} catch {
|
|
65763
|
+
out.push({ name: name2, value });
|
|
65764
|
+
}
|
|
65765
|
+
}
|
|
65766
|
+
return out;
|
|
65767
|
+
}
|
|
65768
|
+
function postDataOf(body) {
|
|
65769
|
+
if (!body || body.mode === "none") return void 0;
|
|
65770
|
+
switch (body.mode) {
|
|
65771
|
+
case "json":
|
|
65772
|
+
return body.json ? { mimeType: "application/json", text: body.json } : void 0;
|
|
65773
|
+
case "raw":
|
|
65774
|
+
return body.raw ? { mimeType: body.rawContentType ?? "text/plain", text: body.raw } : void 0;
|
|
65775
|
+
case "graphql":
|
|
65776
|
+
return body.graphql ? { mimeType: "application/json", text: JSON.stringify(body.graphql) } : void 0;
|
|
65777
|
+
case "soap":
|
|
65778
|
+
return body.soap ? { mimeType: "text/xml", text: body.soap.envelope ?? "" } : void 0;
|
|
65779
|
+
case "form": {
|
|
65780
|
+
const text = (body.form ?? []).filter((p2) => p2.enabled && p2.key).map((p2) => `${encodeURIComponent(p2.key)}=${encodeURIComponent(p2.value)}`).join("&");
|
|
65781
|
+
return { mimeType: "application/x-www-form-urlencoded", text };
|
|
65782
|
+
}
|
|
65783
|
+
default:
|
|
65784
|
+
return void 0;
|
|
65785
|
+
}
|
|
65786
|
+
}
|
|
65787
|
+
function historyToHar(entries, creatorVersion = "1.0") {
|
|
65788
|
+
const harEntries = entries.map((e) => {
|
|
65789
|
+
const post = postDataOf(e.request.body);
|
|
65790
|
+
const contentType = e.response.headers["content-type"] ?? e.response.headers["Content-Type"] ?? "text/plain";
|
|
65791
|
+
return {
|
|
65792
|
+
startedDateTime: new Date(e.timestamp).toISOString(),
|
|
65793
|
+
time: e.response.durationMs,
|
|
65794
|
+
request: {
|
|
65795
|
+
method: e.request.method,
|
|
65796
|
+
url: e.resolvedUrl,
|
|
65797
|
+
httpVersion: "HTTP/1.1",
|
|
65798
|
+
cookies: [],
|
|
65799
|
+
headers: enabledHeaders(e.request.headers),
|
|
65800
|
+
queryString: queryStringOf(e.resolvedUrl),
|
|
65801
|
+
...post ? { postData: post } : {},
|
|
65802
|
+
headersSize: -1,
|
|
65803
|
+
bodySize: post ? post.text.length : 0
|
|
65804
|
+
},
|
|
65805
|
+
response: {
|
|
65806
|
+
status: e.response.status,
|
|
65807
|
+
statusText: e.response.statusText,
|
|
65808
|
+
httpVersion: "HTTP/1.1",
|
|
65809
|
+
cookies: [],
|
|
65810
|
+
headers: Object.entries(e.response.headers).map(([name2, value]) => ({ name: name2, value })),
|
|
65811
|
+
content: {
|
|
65812
|
+
size: e.response.bodySize,
|
|
65813
|
+
mimeType: contentType,
|
|
65814
|
+
text: e.response.body
|
|
65815
|
+
},
|
|
65816
|
+
redirectURL: "",
|
|
65817
|
+
headersSize: -1,
|
|
65818
|
+
bodySize: e.response.bodySize
|
|
65819
|
+
},
|
|
65820
|
+
cache: {},
|
|
65821
|
+
timings: { send: 0, wait: e.response.durationMs, receive: 0 },
|
|
65822
|
+
...e.environmentName ? { comment: `environment: ${e.environmentName}` } : {}
|
|
65823
|
+
};
|
|
65824
|
+
});
|
|
65825
|
+
return JSON.stringify({
|
|
65826
|
+
log: {
|
|
65827
|
+
version: "1.2",
|
|
65828
|
+
creator: { name: "API Spector", version: creatorVersion },
|
|
65829
|
+
entries: harEntries
|
|
65830
|
+
}
|
|
65831
|
+
}, null, 2);
|
|
65832
|
+
}
|
|
65833
|
+
const { electron: electron$g } = window;
|
|
65392
65834
|
const STATUS_COLOR = {
|
|
65393
65835
|
"2": "text-emerald-400",
|
|
65394
65836
|
"3": "text-amber-400",
|
|
@@ -65416,6 +65858,8 @@ function HistoryPanel() {
|
|
|
65416
65858
|
const clearHistory = useStore((s) => s.clearHistory);
|
|
65417
65859
|
const activeTabId = useStore((s) => s.activeTabId);
|
|
65418
65860
|
const setTabResponse = useStore((s) => s.setTabResponse);
|
|
65861
|
+
const setActiveRequest = useStore((s) => s.setActiveRequest);
|
|
65862
|
+
const collections = useStore((s) => s.collections);
|
|
65419
65863
|
const [selected, setSelected] = reactExports.useState(null);
|
|
65420
65864
|
const [search, setSearch] = reactExports.useState("");
|
|
65421
65865
|
const filtered = search ? history2.filter(
|
|
@@ -65431,9 +65875,18 @@ function HistoryPanel() {
|
|
|
65431
65875
|
groups.push({ label, entries: [entry] });
|
|
65432
65876
|
}
|
|
65433
65877
|
}
|
|
65878
|
+
async function downloadHar() {
|
|
65879
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/[:T]/g, "-");
|
|
65880
|
+
await electron$g.saveResults(historyToHar(history2), `api-spector-history-${stamp}.har`);
|
|
65881
|
+
}
|
|
65434
65882
|
function open(entry) {
|
|
65435
65883
|
setSelected(entry);
|
|
65436
|
-
|
|
65884
|
+
const stillExists = Object.values(collections).some((c) => entry.request.id in c.data.requests);
|
|
65885
|
+
if (stillExists) {
|
|
65886
|
+
setActiveRequest(entry.request.id);
|
|
65887
|
+
const tabId = useStore.getState().activeTabId;
|
|
65888
|
+
if (tabId) setTabResponse(tabId, entry.response, entry.scriptResult ?? null);
|
|
65889
|
+
} else if (activeTabId) {
|
|
65437
65890
|
setTabResponse(activeTabId, entry.response, entry.scriptResult ?? null);
|
|
65438
65891
|
}
|
|
65439
65892
|
}
|
|
@@ -65448,18 +65901,29 @@ function HistoryPanel() {
|
|
|
65448
65901
|
className: "flex-1 bg-surface-800 rounded px-2 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-blue-500"
|
|
65449
65902
|
}
|
|
65450
65903
|
),
|
|
65451
|
-
history2.length > 0 && /* @__PURE__ */ jsxRuntimeExports.
|
|
65452
|
-
|
|
65453
|
-
|
|
65454
|
-
|
|
65455
|
-
|
|
65456
|
-
|
|
65457
|
-
|
|
65458
|
-
|
|
65459
|
-
|
|
65460
|
-
|
|
65461
|
-
|
|
65462
|
-
|
|
65904
|
+
history2.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
65905
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
65906
|
+
"button",
|
|
65907
|
+
{
|
|
65908
|
+
onClick: downloadHar,
|
|
65909
|
+
className: "text-xs text-surface-400 hover:text-surface-100 transition-colors px-1",
|
|
65910
|
+
title: "Download history as a HAR file",
|
|
65911
|
+
children: "HAR"
|
|
65912
|
+
}
|
|
65913
|
+
),
|
|
65914
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
65915
|
+
"button",
|
|
65916
|
+
{
|
|
65917
|
+
onClick: () => {
|
|
65918
|
+
clearHistory();
|
|
65919
|
+
setSelected(null);
|
|
65920
|
+
},
|
|
65921
|
+
className: "text-xs text-surface-400 hover:text-red-400 transition-colors px-1",
|
|
65922
|
+
title: "Clear all history",
|
|
65923
|
+
children: "Clear"
|
|
65924
|
+
}
|
|
65925
|
+
)
|
|
65926
|
+
] })
|
|
65463
65927
|
] }),
|
|
65464
65928
|
history2.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 flex items-center justify-center text-xs text-surface-400 px-4 text-center", children: "No history yet. Send a request to start recording." }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto", children: [
|
|
65465
65929
|
filtered.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "px-3 py-4 text-xs text-surface-400", children: "No matches." }),
|
|
@@ -66241,6 +66705,7 @@ function WorkspaceSettingsModal({ onClose }) {
|
|
|
66241
66705
|
const existing = workspace?.settings ?? {};
|
|
66242
66706
|
const [activeTab, setActiveTab] = reactExports.useState("general");
|
|
66243
66707
|
const [defaultEnvironment, setDefaultEnvironment] = reactExports.useState(existing.defaultEnvironment ?? "");
|
|
66708
|
+
const [persistHistory, setPersistHistory] = reactExports.useState(existing.persistHistory ?? false);
|
|
66244
66709
|
const [proxyUrl, setProxyUrl] = reactExports.useState(existing.proxy?.url ?? "");
|
|
66245
66710
|
const [proxyUser, setProxyUser] = reactExports.useState(existing.proxy?.auth?.username ?? "");
|
|
66246
66711
|
const [proxyPass, setProxyPass] = reactExports.useState(existing.proxy?.auth?.password ?? "");
|
|
@@ -66287,9 +66752,15 @@ function WorkspaceSettingsModal({ onClose }) {
|
|
|
66287
66752
|
else delete settings.dashboardUrl;
|
|
66288
66753
|
if (defaultEnvironment) settings.defaultEnvironment = defaultEnvironment;
|
|
66289
66754
|
else delete settings.defaultEnvironment;
|
|
66755
|
+
if (persistHistory) settings.persistHistory = true;
|
|
66756
|
+
else delete settings.persistHistory;
|
|
66290
66757
|
updateWorkspaceSettings(settings);
|
|
66291
66758
|
const updated = useStore.getState().workspace;
|
|
66292
66759
|
if (updated) await electron$b.saveWorkspace(updated);
|
|
66760
|
+
if (persistHistory) {
|
|
66761
|
+
await electron$b.saveHistory(useStore.getState().history).catch(() => {
|
|
66762
|
+
});
|
|
66763
|
+
}
|
|
66293
66764
|
onClose();
|
|
66294
66765
|
}
|
|
66295
66766
|
function zoomStep(dir) {
|
|
@@ -66339,7 +66810,22 @@ function WorkspaceSettingsModal({ onClose }) {
|
|
|
66339
66810
|
}
|
|
66340
66811
|
)
|
|
66341
66812
|
] }),
|
|
66342
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-600 text-[11px]", children: "CLI runs without --environment use this environment, and the app selects it when no environment is active." })
|
|
66813
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-600 text-[11px]", children: "CLI runs without --environment use this environment, and the app selects it when no environment is active." }),
|
|
66814
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-start gap-2 mt-2 cursor-pointer", children: [
|
|
66815
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
66816
|
+
"input",
|
|
66817
|
+
{
|
|
66818
|
+
type: "checkbox",
|
|
66819
|
+
checked: persistHistory,
|
|
66820
|
+
onChange: (e) => setPersistHistory(e.target.checked),
|
|
66821
|
+
className: "mt-0.5"
|
|
66822
|
+
}
|
|
66823
|
+
),
|
|
66824
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex flex-col gap-0.5", children: [
|
|
66825
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-200", children: "Persist request history" }),
|
|
66826
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-600 text-[11px]", children: "Save history to history.json in the workspace folder so it survives restarts. The file is gitignored. Off by default; history stays in memory otherwise." })
|
|
66827
|
+
] })
|
|
66828
|
+
] })
|
|
66343
66829
|
] }),
|
|
66344
66830
|
activeTab === "appearance" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
66345
66831
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
@@ -71568,7 +72054,7 @@ function App() {
|
|
|
71568
72054
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#6aa3c8" }, children: "Spector" }),
|
|
71569
72055
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ml-2 text-[10px] font-normal opacity-50", children: [
|
|
71570
72056
|
"v",
|
|
71571
|
-
"0.3.
|
|
72057
|
+
"0.3.8"
|
|
71572
72058
|
] }),
|
|
71573
72059
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mx-2 opacity-30", children: "·" }),
|
|
71574
72060
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|