@testsmith/api-spector 0.6.3 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/out/main/agents.js +5 -5
  2. package/out/main/chunks/{broker-client-WEymdqAK.js → broker-client-CvRuiX3o.js} +26 -23
  3. package/out/main/chunks/{cli-common-vASoDfQH.js → cli-common-Cq_cphpZ.js} +4 -4
  4. package/out/main/chunks/{coverage-C54mZEh_.js → coverage-C0U4QM83.js} +8 -10
  5. package/out/main/chunks/{data-files-Br2h3Dt2.js → data-files-BYqhsoPe.js} +4 -4
  6. package/out/main/chunks/escape-DDVAlOvZ.js +9 -0
  7. package/out/main/chunks/{import-DdXhO56X.js → import-DYssQzex.js} +4 -4
  8. package/out/main/chunks/{mock-server-BxiXhP1m.js → mock-server-C4kTFkod.js} +9 -11
  9. package/out/main/chunks/{recorder-0Ij921El.js → recorder-BkO8G_mB.js} +2 -2
  10. package/out/main/chunks/{request-collection-GzK4QgvT.js → request-collection-KGPuvN7o.js} +2 -2
  11. package/out/main/chunks/{request-exec-DAFA6Fap.js → request-exec-BRmVweue.js} +41 -41
  12. package/out/main/chunks/{soap-handler-BAPCALkN.js → soap-handler-BznuMFIP.js} +7 -9
  13. package/out/main/compare.js +10 -10
  14. package/out/main/contract.js +18 -20
  15. package/out/main/coverage.js +7 -7
  16. package/out/main/generate-tests.js +9 -9
  17. package/out/main/index.js +163 -263
  18. package/out/main/lib.js +6 -7
  19. package/out/main/mock.js +8 -8
  20. package/out/main/record.js +9 -9
  21. package/out/main/runner.js +14 -15
  22. package/out/main/wsdl.js +9 -9
  23. package/out/preload/index.js +1 -1
  24. package/out/renderer/assets/{GraphQLEditor-MImdgXb4.js → GraphQLEditor-Bih8eb_r.js} +6 -6
  25. package/out/renderer/assets/{SoapEditor-rxb3heue.js → SoapEditor-D1sQKu7l.js} +3 -3
  26. package/out/renderer/assets/{index-ByGIqJ2H.js → index-vEDOfWlE.js} +380 -269
  27. package/out/renderer/index.html +1 -1
  28. package/package.json +1 -1
  29. package/out/main/chunks/escape-BwPM4Tf6.js +0 -9
@@ -1,4 +1,4 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./GraphQLEditor-MImdgXb4.js","./codemirror-F6m3Hm1M.js","./ajv-2MAg6kGF.js","./SoapEditor-rxb3heue.js"])))=>i.map(i=>d[i]);
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./GraphQLEditor-Bih8eb_r.js","./codemirror-F6m3Hm1M.js","./ajv-2MAg6kGF.js","./SoapEditor-D1sQKu7l.js"])))=>i.map(i=>d[i]);
2
2
  import { r as reactExports, g as getDefaultExportFromCjs, R as React$2, a as autocompletion, h as hoverTooltip, j as jsxRuntimeExports, k as keymap, t as toggleComment, x as xml, b as json, c as ReactCodeMirror, o as oneDark, d as javascript } from "./codemirror-F6m3Hm1M.js";
3
3
  import { A as Ajv } from "./ajv-2MAg6kGF.js";
4
4
  var client = { exports: {} };
@@ -12797,132 +12797,6 @@ const immerImpl = (initializer) => (set2, get2, store) => {
12797
12797
  return initializer(store.setState, get2, store);
12798
12798
  };
12799
12799
  const immer = immerImpl;
12800
- function uniqueName(base, existing) {
12801
- if (!existing.includes(base)) return base;
12802
- let i = 2;
12803
- while (existing.includes(`${base} (${i})`)) i++;
12804
- return `${base} (${i})`;
12805
- }
12806
- function colRelPath(name, id) {
12807
- const safe = safeName(name) || id.slice(0, 8);
12808
- return `collections/${safe}.spector`;
12809
- }
12810
- function envRelPath(name, id) {
12811
- const safe = safeName(name) || id;
12812
- return `environments/${safe}.env.json`;
12813
- }
12814
- function safeName(name) {
12815
- return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-_]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
12816
- }
12817
- let getRandomValues;
12818
- const rnds8 = new Uint8Array(16);
12819
- function rng() {
12820
- if (!getRandomValues) {
12821
- getRandomValues = typeof crypto !== "undefined" && crypto.getRandomValues && crypto.getRandomValues.bind(crypto);
12822
- if (!getRandomValues) {
12823
- throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
12824
- }
12825
- }
12826
- return getRandomValues(rnds8);
12827
- }
12828
- const byteToHex = [];
12829
- for (let i = 0; i < 256; ++i) {
12830
- byteToHex.push((i + 256).toString(16).slice(1));
12831
- }
12832
- function unsafeStringify(arr, offset = 0) {
12833
- return byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]];
12834
- }
12835
- const randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
12836
- const native = {
12837
- randomUUID
12838
- };
12839
- function v4(options, buf, offset) {
12840
- if (native.randomUUID && true && !options) {
12841
- return native.randomUUID();
12842
- }
12843
- options = options || {};
12844
- const rnds = options.random || (options.rng || rng)();
12845
- rnds[6] = rnds[6] & 15 | 64;
12846
- rnds[8] = rnds[8] & 63 | 128;
12847
- return unsafeStringify(rnds);
12848
- }
12849
- function orderedChildren(folder2) {
12850
- const reqSet = new Set(folder2.requestIds);
12851
- const folderSet = new Set(folder2.folders.map((f) => f.id));
12852
- const seen2 = /* @__PURE__ */ new Set();
12853
- const out = [];
12854
- for (const ref of folder2.childOrder ?? []) {
12855
- const key = `${ref.type}:${ref.id}`;
12856
- if (seen2.has(key)) continue;
12857
- if (ref.type === "request" && reqSet.has(ref.id) || ref.type === "folder" && folderSet.has(ref.id)) {
12858
- out.push({ type: ref.type, id: ref.id });
12859
- seen2.add(key);
12860
- }
12861
- }
12862
- for (const id of folder2.requestIds) if (!seen2.has(`request:${id}`)) out.push({ type: "request", id });
12863
- for (const f of folder2.folders) if (!seen2.has(`folder:${f.id}`)) out.push({ type: "folder", id: f.id });
12864
- return out;
12865
- }
12866
- function findFolder$1(root2, id) {
12867
- if (root2.id === id) return root2;
12868
- for (const sub of root2.folders) {
12869
- const found = findFolder$1(sub, id);
12870
- if (found) return found;
12871
- }
12872
- return null;
12873
- }
12874
- function findFolderContaining(root2, reqId) {
12875
- if (root2.requestIds.includes(reqId)) return root2;
12876
- for (const sub of root2.folders) {
12877
- const found = findFolderContaining(sub, reqId);
12878
- if (found) return found;
12879
- }
12880
- return null;
12881
- }
12882
- function removeFromFolder(root2, reqId) {
12883
- root2.requestIds = root2.requestIds.filter((id) => id !== reqId);
12884
- root2.folders.forEach((sub) => removeFromFolder(sub, reqId));
12885
- }
12886
- function removeFolderById(parent, folderId) {
12887
- const idx = parent.folders.findIndex((f) => f.id === folderId);
12888
- if (idx !== -1) {
12889
- parent.folders.splice(idx, 1);
12890
- return true;
12891
- }
12892
- for (const sub of parent.folders) {
12893
- if (removeFolderById(sub, folderId)) return true;
12894
- }
12895
- return false;
12896
- }
12897
- function findFolderParent(root2, folderId) {
12898
- for (const sub of root2.folders) {
12899
- if (sub.id === folderId) return root2;
12900
- const found = findFolderParent(sub, folderId);
12901
- if (found) return found;
12902
- }
12903
- return null;
12904
- }
12905
- function findFolderPath(root2, requestId) {
12906
- if (root2.requestIds.includes(requestId)) return [root2];
12907
- for (const sub of root2.folders) {
12908
- const path = findFolderPath(sub, requestId);
12909
- if (path.length > 0) return [root2, ...path];
12910
- }
12911
- return [];
12912
- }
12913
- function collectRequestIds$1(f) {
12914
- return [...f.requestIds, ...f.folders.flatMap(collectRequestIds$1)];
12915
- }
12916
- function reIdFolderTree(root2, mapRequestId) {
12917
- root2.id = v4();
12918
- root2.requestIds = root2.requestIds.map(mapRequestId);
12919
- root2.folders.forEach((sub) => reIdFolderTree(sub, mapRequestId));
12920
- }
12921
- function cloneAndReId(folder2, mapRequestId) {
12922
- const cloned = JSON.parse(JSON.stringify(folder2));
12923
- reIdFolderTree(cloned, mapRequestId);
12924
- return cloned;
12925
- }
12926
12800
  const WS_MESSAGE_CAP = 1e3;
12927
12801
  const createWsSlice = (set2) => ({
12928
12802
  wsConnections: {},
@@ -12988,7 +12862,7 @@ const createStreamSlice = (set2) => ({
12988
12862
  s.liveStream = { streamId, tabId, events: [], streaming: true };
12989
12863
  }),
12990
12864
  pushLiveStreamEvents: (streamId, events) => set2((s) => {
12991
- if (!s.liveStream || s.liveStream.streamId !== streamId) return;
12865
+ if (s.liveStream?.streamId !== streamId) return;
12992
12866
  s.liveStream.events.push(...events);
12993
12867
  const overflow = s.liveStream.events.length - LIVE_EVENT_CAP;
12994
12868
  if (overflow > 0) s.liveStream.events.splice(0, overflow);
@@ -13088,7 +12962,7 @@ const createContractSlice = (set2) => ({
13088
12962
  loadContractSnapshot: (relPath, snapshot) => set2((s) => {
13089
12963
  s.contractSnapshots[relPath] = snapshot;
13090
12964
  if (s.workspace) {
13091
- if (!s.workspace.contracts) s.workspace.contracts = [];
12965
+ s.workspace.contracts ??= [];
13092
12966
  if (!s.workspace.contracts.includes(relPath)) s.workspace.contracts.push(relPath);
13093
12967
  }
13094
12968
  }),
@@ -13111,6 +12985,38 @@ const createContractSlice = (set2) => ({
13111
12985
  s.contractDesignerSeed = null;
13112
12986
  })
13113
12987
  });
12988
+ let getRandomValues;
12989
+ const rnds8 = new Uint8Array(16);
12990
+ function rng() {
12991
+ if (!getRandomValues) {
12992
+ getRandomValues = typeof crypto !== "undefined" && crypto.getRandomValues && crypto.getRandomValues.bind(crypto);
12993
+ if (!getRandomValues) {
12994
+ throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
12995
+ }
12996
+ }
12997
+ return getRandomValues(rnds8);
12998
+ }
12999
+ const byteToHex = [];
13000
+ for (let i = 0; i < 256; ++i) {
13001
+ byteToHex.push((i + 256).toString(16).slice(1));
13002
+ }
13003
+ function unsafeStringify(arr, offset = 0) {
13004
+ return byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]];
13005
+ }
13006
+ const randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
13007
+ const native = {
13008
+ randomUUID
13009
+ };
13010
+ function v4(options, buf, offset) {
13011
+ if (native.randomUUID && true && !options) {
13012
+ return native.randomUUID();
13013
+ }
13014
+ options = options || {};
13015
+ const rnds = options.random || (options.rng || rng)();
13016
+ rnds[6] = rnds[6] & 15 | 64;
13017
+ rnds[8] = rnds[8] & 63 | 128;
13018
+ return unsafeStringify(rnds);
13019
+ }
13114
13020
  function makeTab(requestId, collectionId, opts = {}) {
13115
13021
  return {
13116
13022
  id: v4(),
@@ -13241,6 +13147,100 @@ const createTabsSlice = (set2, get2) => ({
13241
13147
  if (tab) tab.requestTab = t;
13242
13148
  })
13243
13149
  });
13150
+ function uniqueName(base, existing) {
13151
+ if (!existing.includes(base)) return base;
13152
+ let i = 2;
13153
+ while (existing.includes(`${base} (${i})`)) i++;
13154
+ return `${base} (${i})`;
13155
+ }
13156
+ function colRelPath(name, id) {
13157
+ const safe = safeName(name) || id.slice(0, 8);
13158
+ return `collections/${safe}.spector`;
13159
+ }
13160
+ function envRelPath(name, id) {
13161
+ const safe = safeName(name) || id;
13162
+ return `environments/${safe}.env.json`;
13163
+ }
13164
+ function safeName(name) {
13165
+ return name.trim().toLowerCase().replaceAll(/\s+/g, "-").replaceAll(/[^a-z0-9-_]/g, "").replaceAll(/-+/g, "-").replaceAll(/^-|-$/g, "");
13166
+ }
13167
+ function orderedChildren(folder2) {
13168
+ const reqSet = new Set(folder2.requestIds);
13169
+ const folderSet = new Set(folder2.folders.map((f) => f.id));
13170
+ const seen2 = /* @__PURE__ */ new Set();
13171
+ const out = [];
13172
+ for (const ref of folder2.childOrder ?? []) {
13173
+ const key = `${ref.type}:${ref.id}`;
13174
+ if (seen2.has(key)) continue;
13175
+ if (ref.type === "request" && reqSet.has(ref.id) || ref.type === "folder" && folderSet.has(ref.id)) {
13176
+ out.push({ type: ref.type, id: ref.id });
13177
+ seen2.add(key);
13178
+ }
13179
+ }
13180
+ for (const id of folder2.requestIds) if (!seen2.has(`request:${id}`)) out.push({ type: "request", id });
13181
+ for (const f of folder2.folders) if (!seen2.has(`folder:${f.id}`)) out.push({ type: "folder", id: f.id });
13182
+ return out;
13183
+ }
13184
+ function findFolder$1(root2, id) {
13185
+ if (root2.id === id) return root2;
13186
+ for (const sub of root2.folders) {
13187
+ const found = findFolder$1(sub, id);
13188
+ if (found) return found;
13189
+ }
13190
+ return null;
13191
+ }
13192
+ function findFolderContaining(root2, reqId) {
13193
+ if (root2.requestIds.includes(reqId)) return root2;
13194
+ for (const sub of root2.folders) {
13195
+ const found = findFolderContaining(sub, reqId);
13196
+ if (found) return found;
13197
+ }
13198
+ return null;
13199
+ }
13200
+ function removeFromFolder(root2, reqId) {
13201
+ root2.requestIds = root2.requestIds.filter((id) => id !== reqId);
13202
+ root2.folders.forEach((sub) => removeFromFolder(sub, reqId));
13203
+ }
13204
+ function removeFolderById(parent, folderId) {
13205
+ const idx = parent.folders.findIndex((f) => f.id === folderId);
13206
+ if (idx !== -1) {
13207
+ parent.folders.splice(idx, 1);
13208
+ return true;
13209
+ }
13210
+ for (const sub of parent.folders) {
13211
+ if (removeFolderById(sub, folderId)) return true;
13212
+ }
13213
+ return false;
13214
+ }
13215
+ function findFolderParent(root2, folderId) {
13216
+ for (const sub of root2.folders) {
13217
+ if (sub.id === folderId) return root2;
13218
+ const found = findFolderParent(sub, folderId);
13219
+ if (found) return found;
13220
+ }
13221
+ return null;
13222
+ }
13223
+ function findFolderPath(root2, requestId) {
13224
+ if (root2.requestIds.includes(requestId)) return [root2];
13225
+ for (const sub of root2.folders) {
13226
+ const path = findFolderPath(sub, requestId);
13227
+ if (path.length > 0) return [root2, ...path];
13228
+ }
13229
+ return [];
13230
+ }
13231
+ function collectRequestIds$1(f) {
13232
+ return [...f.requestIds, ...f.folders.flatMap(collectRequestIds$1)];
13233
+ }
13234
+ function reIdFolderTree(root2, mapRequestId) {
13235
+ root2.id = v4();
13236
+ root2.requestIds = root2.requestIds.map(mapRequestId);
13237
+ root2.folders.forEach((sub) => reIdFolderTree(sub, mapRequestId));
13238
+ }
13239
+ function cloneAndReId(folder2, mapRequestId) {
13240
+ const cloned = structuredClone(folder2);
13241
+ reIdFolderTree(cloned, mapRequestId);
13242
+ return cloned;
13243
+ }
13244
13244
  const WRITE_METHODS$1 = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH"]);
13245
13245
  function newId() {
13246
13246
  try {
@@ -13250,7 +13250,7 @@ function newId() {
13250
13250
  }
13251
13251
  }
13252
13252
  function clone(base, patch) {
13253
- const copy2 = JSON.parse(JSON.stringify(base));
13253
+ const copy2 = structuredClone(base);
13254
13254
  copy2.id = newId();
13255
13255
  delete copy2.examples;
13256
13256
  delete copy2.contract;
@@ -13482,8 +13482,8 @@ const createCollectionsSlice = (set2, get2) => ({
13482
13482
  s.tabs = s.tabs.filter((t) => t.collectionId !== id);
13483
13483
  if (s.activeCollectionId === id) {
13484
13484
  s.activeCollectionId = Object.keys(s.collections)[0] ?? null;
13485
- const activeTab = s.tabs.find((t) => t.id === s.activeTabId);
13486
- if (!activeTab) s.activeTabId = s.tabs[0]?.id ?? null;
13485
+ const hasActiveTab = s.tabs.some((t) => t.id === s.activeTabId);
13486
+ if (!hasActiveTab) s.activeTabId = s.tabs[0]?.id ?? null;
13487
13487
  }
13488
13488
  });
13489
13489
  },
@@ -13629,7 +13629,7 @@ const createCollectionsSlice = (set2, get2) => ({
13629
13629
  const entry = Object.values(s.collections).find((c) => c.data.requests[requestId]);
13630
13630
  const ex = entry?.data.requests[requestId].examples?.find((e) => e.id === exampleId);
13631
13631
  if (!entry || !ex) return;
13632
- ex.request = { ...ex.request ?? {}, ...patch };
13632
+ ex.request = { ...ex.request, ...patch };
13633
13633
  entry.dirty = true;
13634
13634
  }),
13635
13635
  updateExampleResponse: (requestId, exampleId, response) => set2((s) => {
@@ -13711,7 +13711,7 @@ const createCollectionsSlice = (set2, get2) => ({
13711
13711
  }),
13712
13712
  duplicateRequest: (collectionId, id) => set2((s) => {
13713
13713
  const col = s.collections[collectionId]?.data;
13714
- if (!col || !col.requests[id]) return;
13714
+ if (!col?.requests[id]) return;
13715
13715
  const orig = col.requests[id];
13716
13716
  const copy2 = { ...JSON.parse(JSON.stringify(orig)), id: v4(), name: orig.name + " (copy)" };
13717
13717
  col.requests[copy2.id] = copy2;
@@ -13834,7 +13834,7 @@ const createCollectionsSlice = (set2, get2) => ({
13834
13834
  const entry = Object.values(s.collections).find((c) => c.data.requests[requestId]);
13835
13835
  if (!entry) return;
13836
13836
  const req = entry.data.requests[requestId];
13837
- req.meta = { ...req.meta ?? {}, tags };
13837
+ req.meta = { ...req.meta, tags };
13838
13838
  entry.dirty = true;
13839
13839
  }),
13840
13840
  // ── Folder settings ─────────────────────────────────────────────────────────
@@ -13882,7 +13882,7 @@ const STORAGE_KEYS = {
13882
13882
  updateSkipped: "apiSpectorUpdateSkipped"
13883
13883
  };
13884
13884
  function envSlugRelPath(name) {
13885
- return `environments/${name.toLowerCase().replace(/[^a-z0-9]+/g, "-")}.env.json`;
13885
+ return `environments/${name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-")}.env.json`;
13886
13886
  }
13887
13887
  const createEnvironmentsSlice = (set2, get2) => ({
13888
13888
  environments: {},
@@ -13982,7 +13982,7 @@ const createMocksSlice = (set2, get2) => ({
13982
13982
  s.mocks[mock.id] = { relPath, data: mock, running: false };
13983
13983
  s.activeMockId = mock.id;
13984
13984
  if (s.workspace) {
13985
- if (!s.workspace.mocks) s.workspace.mocks = [];
13985
+ s.workspace.mocks ??= [];
13986
13986
  s.workspace.mocks.push(relPath);
13987
13987
  }
13988
13988
  }),
@@ -14045,7 +14045,7 @@ const createUiSlice = (set2) => ({
14045
14045
  s.theme = t;
14046
14046
  localStorage.setItem(STORAGE_KEYS.theme, t);
14047
14047
  if (s.workspace) {
14048
- if (!s.workspace.settings) s.workspace.settings = {};
14048
+ s.workspace.settings ??= {};
14049
14049
  s.workspace.settings.theme = t;
14050
14050
  }
14051
14051
  if (t === "system") {
@@ -14184,7 +14184,7 @@ const useStore = create()(
14184
14184
  if (activeColId && s.collections[activeColId]) {
14185
14185
  const col = s.collections[activeColId].data;
14186
14186
  col.collectionVariables = {
14187
- ...col.collectionVariables ?? {},
14187
+ ...col.collectionVariables,
14188
14188
  ...safeColVars
14189
14189
  };
14190
14190
  s.collections[activeColId].dirty = true;
@@ -14972,7 +14972,7 @@ function useVarNames() {
14972
14972
  }
14973
14973
  const envVars = activeEnv?.variables ?? [];
14974
14974
  envVars.filter((v) => v.enabled && v.key).forEach((v) => names.add(v.key));
14975
- return [...DYNAMIC_VAR_NAMES, ...Array.from(names).sort()];
14975
+ return [...DYNAMIC_VAR_NAMES, ...Array.from(names).sort((a, b) => a.localeCompare(b))];
14976
14976
  }, [
14977
14977
  activeEnv,
14978
14978
  activeCollectionId,
@@ -23837,7 +23837,7 @@ function detectLocale() {
23837
23837
  }
23838
23838
  function interpolate(str, vars) {
23839
23839
  if (!vars) return str;
23840
- return str.replace(/:([a-zA-Z][a-zA-Z0-9]*)/g, (m, k) => k in vars ? String(vars[k]) : m);
23840
+ return str.replaceAll(/:([a-zA-Z][a-zA-Z0-9]*)/g, (m, k) => k in vars ? String(vars[k]) : m);
23841
23841
  }
23842
23842
  function translate(locale, key, vars) {
23843
23843
  let msg = CATALOGS[locale]?.[key] ?? key;
@@ -23872,7 +23872,8 @@ function I18nProvider({ children }) {
23872
23872
  setLocaleState(l);
23873
23873
  }, []);
23874
23874
  const t = reactExports.useCallback((key, vars) => translate(locale, key, vars), [locale]);
23875
- return /* @__PURE__ */ jsxRuntimeExports.jsx(I18nContext.Provider, { value: { locale, setLocale, t }, children });
23875
+ const ctx = reactExports.useMemo(() => ({ locale, setLocale, t }), [locale, setLocale, t]);
23876
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(I18nContext.Provider, { value: ctx, children });
23876
23877
  }
23877
23878
  function useT() {
23878
23879
  return reactExports.useContext(I18nContext).t;
@@ -24359,18 +24360,34 @@ function Modal({
24359
24360
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
24360
24361
  "div",
24361
24362
  {
24363
+ role: closeOnBackdrop ? "button" : void 0,
24364
+ tabIndex: closeOnBackdrop ? -1 : void 0,
24365
+ "aria-label": closeOnBackdrop ? "Close dialog" : void 0,
24362
24366
  className: `fixed inset-0 ${overlayClassName}`,
24363
- onClick: closeOnBackdrop ? onClose : void 0,
24364
- children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: panelClassName, onClick: (e) => e.stopPropagation(), children: [
24365
- title !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between px-4 py-2.5 border-b border-surface-800 flex-shrink-0", children: [
24366
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
24367
- /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-sm font-semibold", children: title }),
24368
- subtitle !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-surface-400 mt-0.5", children: subtitle })
24369
- ] }),
24370
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, className: "text-surface-400 hover:text-[var(--text-primary)] text-lg leading-none", children: "×" })
24371
- ] }),
24372
- children
24373
- ] })
24367
+ onClick: closeOnBackdrop ? (e) => {
24368
+ if (e.target === e.currentTarget) onClose();
24369
+ } : void 0,
24370
+ onKeyDown: closeOnBackdrop ? (e) => {
24371
+ if (e.key === "Escape") onClose();
24372
+ } : void 0,
24373
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
24374
+ "div",
24375
+ {
24376
+ role: "dialog",
24377
+ "aria-modal": "true",
24378
+ className: panelClassName,
24379
+ children: [
24380
+ title !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between px-4 py-2.5 border-b border-surface-800 flex-shrink-0", children: [
24381
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
24382
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-sm font-semibold", children: title }),
24383
+ subtitle !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-surface-400 mt-0.5", children: subtitle })
24384
+ ] }),
24385
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, className: "text-surface-400 hover:text-[var(--text-primary)] text-lg leading-none", children: "×" })
24386
+ ] }),
24387
+ children
24388
+ ]
24389
+ }
24390
+ )
24374
24391
  }
24375
24392
  );
24376
24393
  }
@@ -25053,7 +25070,7 @@ function normalisePath(url) {
25053
25070
  let path = url.replace(/^\{\{[^}]+\}\}/, "").replace(/^https?:\/\/[^/]+/, "");
25054
25071
  if (!path.startsWith("/")) path = "/" + path;
25055
25072
  path = path.split("?")[0];
25056
- path = path.replace(/\{\{([^}]+)\}\}/g, "{$1}");
25073
+ path = path.replaceAll(/\{\{([^}]+)\}\}/g, "{$1}");
25057
25074
  return path;
25058
25075
  }
25059
25076
  function collectRequestIds(folder2) {
@@ -25499,7 +25516,7 @@ function collectAllTags(folder2, requests) {
25499
25516
  f.folders.forEach(walk2);
25500
25517
  }
25501
25518
  walk2(folder2);
25502
- return Array.from(tags).sort();
25519
+ return Array.from(tags).sort((a, b) => a.localeCompare(b));
25503
25520
  }
25504
25521
  function folderPathTo(root2, requestId) {
25505
25522
  if (root2.requestIds.includes(requestId)) return [root2];
@@ -25522,11 +25539,12 @@ function getHooksForRequest(requestId, collection2) {
25522
25539
  before.push(...bAll, ...bEach);
25523
25540
  afterReversed.push(...aEach, ...aAll);
25524
25541
  }
25525
- return { before, after: afterReversed.reverse() };
25542
+ afterReversed.reverse();
25543
+ return { before, after: afterReversed };
25526
25544
  }
25527
25545
  function authIsConfigured(auth) {
25528
25546
  if (!auth || auth.type === "none") return false;
25529
- const set2 = (s) => !!(s && s.trim());
25547
+ const set2 = (s) => !!s?.trim();
25530
25548
  switch (auth.type) {
25531
25549
  case "bearer":
25532
25550
  return set2(auth.token) || set2(auth.tokenSecretRef);
@@ -25642,7 +25660,7 @@ function parseDataRow(columns, row) {
25642
25660
  const key = col.toLowerCase();
25643
25661
  const val = row[ci] ?? "";
25644
25662
  if (RESERVED_STATUS.has(key)) {
25645
- const nums = val.split(/[\s,|]+/).map((x) => parseInt(x, 10)).filter((n) => Number.isFinite(n));
25663
+ const nums = val.split(/[\s,|]+/).map((x) => Number.parseInt(x, 10)).filter((n) => Number.isFinite(n));
25646
25664
  if (nums.length) expectStatus = nums;
25647
25665
  } else if (RESERVED_OWASP.has(key)) {
25648
25666
  if (val.trim()) owaspTag = val.trim().toUpperCase();
@@ -25727,7 +25745,7 @@ async function pushRequestAsMonitor(request2, opts) {
25727
25745
  const s = useStore.getState();
25728
25746
  const colEntry = Object.values(s.collections).find((c) => c.data.requests[request2.id]);
25729
25747
  const collectionVars = {
25730
- ...colEntry?.data.collectionVariables ?? {},
25748
+ ...colEntry?.data.collectionVariables,
25731
25749
  ...s.getInheritedVariables(request2.id),
25732
25750
  ...s.sessionVars
25733
25751
  };
@@ -25949,12 +25967,13 @@ function InlineEdit({
25949
25967
  }
25950
25968
  onCommit(trimmed);
25951
25969
  }
25952
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { onClick: (e) => e.stopPropagation(), children: [
25970
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
25953
25971
  /* @__PURE__ */ jsxRuntimeExports.jsx(
25954
25972
  "input",
25955
25973
  {
25956
25974
  ref,
25957
25975
  value: draft,
25976
+ onClick: (e) => e.stopPropagation(),
25958
25977
  onChange: (e) => {
25959
25978
  setDraft(e.target.value);
25960
25979
  setError(null);
@@ -26046,6 +26065,8 @@ function ContextMenu({ items, x, y, onClose }) {
26046
26065
  "div",
26047
26066
  {
26048
26067
  ref,
26068
+ role: "menu",
26069
+ tabIndex: -1,
26049
26070
  style: { position: "fixed", top: pos.top, left: pos.left, zIndex: 9999 },
26050
26071
  className: "bg-surface-900 border border-surface-700 rounded-lg shadow-2xl py-1 min-w-[170px]",
26051
26072
  onMouseDown: (e) => e.stopPropagation(),
@@ -26096,6 +26117,14 @@ function DotsBtn({ items }) {
26096
26117
  menu && /* @__PURE__ */ jsxRuntimeExports.jsx(ContextMenu, { items, x: menu.x, y: menu.y, onClose: () => setMenu(null) })
26097
26118
  ] });
26098
26119
  }
26120
+ function onActivateKey(handler) {
26121
+ return (e) => {
26122
+ if (e.key === "Enter" || e.key === " ") {
26123
+ e.preventDefault();
26124
+ handler();
26125
+ }
26126
+ };
26127
+ }
26099
26128
  function useToast(durationMs = 3e3) {
26100
26129
  const [toast, setToast] = reactExports.useState(null);
26101
26130
  const timer = reactExports.useRef(null);
@@ -26202,6 +26231,14 @@ function RequestRow({
26202
26231
  "div",
26203
26232
  {
26204
26233
  draggable: true,
26234
+ role: "button",
26235
+ tabIndex: 0,
26236
+ onKeyDown: (e) => {
26237
+ if (e.key === "Enter" || e.key === " ") {
26238
+ e.preventDefault();
26239
+ handleRowClick(e);
26240
+ }
26241
+ },
26205
26242
  className: `group flex items-start gap-1.5 py-1 pr-1 rounded-sm cursor-pointer transition-colors ${disabled ? "opacity-40" : ""} ${selected ? "bg-blue-950/40 ring-1 ring-inset ring-blue-500" : isActive ? "bg-surface-800 text-[var(--text-primary)]" : "text-surface-300 hover:bg-surface-800"}`,
26206
26243
  style: { paddingLeft: indent },
26207
26244
  onClick: handleRowClick,
@@ -26351,7 +26388,10 @@ function ExampleRow({ name, indent, isActive, onOpen, onRename, onDelete, onDupl
26351
26388
  {
26352
26389
  className: `group flex items-center gap-1.5 py-1 pr-1 rounded-sm cursor-pointer transition-colors ${isActive ? "bg-surface-800 text-[var(--text-primary)]" : "text-surface-400 hover:bg-surface-800"}`,
26353
26390
  style: { paddingLeft: indent },
26391
+ role: "button",
26392
+ tabIndex: 0,
26354
26393
  onClick: onOpen,
26394
+ onKeyDown: onActivateKey(onOpen),
26355
26395
  onDoubleClick: () => setRenaming(true),
26356
26396
  children: [
26357
26397
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 text-[8px] font-bold px-1 py-px rounded bg-surface-700 text-surface-300", title: t("Example"), children: "EX" }),
@@ -26467,46 +26507,56 @@ function TagChips({
26467
26507
  setAdding(false);
26468
26508
  onDoneAdding?.();
26469
26509
  }
26470
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-0.5 mt-0.5", onClick: (e) => e.stopPropagation(), children: [
26471
- tags.map((tag) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
26472
- "span",
26473
- {
26474
- className: "flex items-center gap-0.5 px-1 py-px rounded text-[9px] bg-blue-900/50 text-blue-300 border border-blue-800/50",
26475
- children: [
26476
- tag,
26477
- /* @__PURE__ */ jsxRuntimeExports.jsx(
26478
- "button",
26479
- {
26480
- onClick: () => onRemove(tag),
26481
- className: "hover:text-red-400 leading-none ml-0.5",
26482
- children: "×"
26483
- }
26484
- )
26485
- ]
26486
- },
26487
- tag
26488
- )),
26489
- adding ? /* @__PURE__ */ jsxRuntimeExports.jsx(
26490
- "input",
26491
- {
26492
- ref: inputRef,
26493
- value: draft,
26494
- onChange: (e) => setDraft(e.target.value),
26495
- onKeyDown: (e) => {
26496
- if (e.key === "Enter") commit();
26497
- if (e.key === "Escape") {
26498
- setAdding(false);
26499
- setDraft("");
26500
- onDoneAdding?.();
26510
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
26511
+ "div",
26512
+ {
26513
+ role: "group",
26514
+ "aria-label": "Tags",
26515
+ className: "flex flex-wrap items-center gap-0.5 mt-0.5",
26516
+ onClick: (e) => e.stopPropagation(),
26517
+ onKeyDown: (e) => e.stopPropagation(),
26518
+ children: [
26519
+ tags.map((tag) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
26520
+ "span",
26521
+ {
26522
+ className: "flex items-center gap-0.5 px-1 py-px rounded text-[9px] bg-blue-900/50 text-blue-300 border border-blue-800/50",
26523
+ children: [
26524
+ tag,
26525
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
26526
+ "button",
26527
+ {
26528
+ onClick: () => onRemove(tag),
26529
+ className: "hover:text-red-400 leading-none ml-0.5",
26530
+ children: "×"
26531
+ }
26532
+ )
26533
+ ]
26534
+ },
26535
+ tag
26536
+ )),
26537
+ adding ? /* @__PURE__ */ jsxRuntimeExports.jsx(
26538
+ "input",
26539
+ {
26540
+ ref: inputRef,
26541
+ value: draft,
26542
+ onChange: (e) => setDraft(e.target.value),
26543
+ onKeyDown: (e) => {
26544
+ if (e.key === "Enter") commit();
26545
+ if (e.key === "Escape") {
26546
+ setAdding(false);
26547
+ setDraft("");
26548
+ onDoneAdding?.();
26549
+ }
26550
+ e.stopPropagation();
26551
+ },
26552
+ onBlur: commit,
26553
+ className: "w-16 text-[9px] bg-surface-700 rounded px-1 py-px focus:outline-none focus:ring-1 focus:ring-blue-500",
26554
+ placeholder: t("tag…")
26501
26555
  }
26502
- e.stopPropagation();
26503
- },
26504
- onBlur: commit,
26505
- className: "w-16 text-[9px] bg-surface-700 rounded px-1 py-px focus:outline-none focus:ring-1 focus:ring-blue-500",
26506
- placeholder: t("tag…")
26507
- }
26508
- ) : null
26509
- ] });
26556
+ ) : null
26557
+ ]
26558
+ }
26559
+ );
26510
26560
  }
26511
26561
  function methodColor$2(method) {
26512
26562
  switch (method) {
@@ -26603,12 +26653,12 @@ function CollectionTree() {
26603
26653
  } });
26604
26654
  }
26605
26655
  function onDropRequest(destCollectionId, destFolderId, destIndex) {
26606
- if (!dragging || dragging.type !== "request") return;
26656
+ if (dragging?.type !== "request") return;
26607
26657
  moveRequest(dragging.collectionId, dragging.requestId, destCollectionId, destFolderId, destIndex);
26608
26658
  setDragging(null);
26609
26659
  }
26610
26660
  function onDropFolder(destCollectionId, destParentFolderId, destIndex) {
26611
- if (!dragging || dragging.type !== "folder") return;
26661
+ if (dragging?.type !== "folder") return;
26612
26662
  if (dragging.collectionId !== destCollectionId) return;
26613
26663
  moveFolder(dragging.collectionId, dragging.folderId, destParentFolderId, destIndex);
26614
26664
  setDragging(null);
@@ -26831,10 +26881,16 @@ function CollectionNode({
26831
26881
  "div",
26832
26882
  {
26833
26883
  className: `group flex items-center gap-1 px-2 py-1.5 cursor-pointer hover:bg-surface-800 transition-colors ${panelActive ? "bg-surface-800 text-[var(--text-primary)]" : isActive ? "text-[var(--text-primary)]" : "text-surface-400"} ${col.disabled ? "opacity-50" : ""} ${dropOver ? "outline outline-1 outline-blue-500 rounded" : ""}`,
26884
+ role: "button",
26885
+ tabIndex: 0,
26834
26886
  onClick: () => {
26835
26887
  onSelectCollection();
26836
26888
  setExpanded((e) => !e);
26837
26889
  },
26890
+ onKeyDown: onActivateKey(() => {
26891
+ onSelectCollection();
26892
+ setExpanded((e) => !e);
26893
+ }),
26838
26894
  onDragOver: dragCtx.dragging ? (e) => {
26839
26895
  e.preventDefault();
26840
26896
  setDropOver(true);
@@ -26990,10 +27046,16 @@ function FolderRow({
26990
27046
  draggable: true,
26991
27047
  className: `group flex items-center gap-1 py-1 transition-colors cursor-pointer ${isActive ? "bg-surface-800 text-[var(--text-primary)]" : "text-surface-400 hover:bg-surface-800"} ${folder2.disabled ? "opacity-40" : ""} ${dropInside ? "outline outline-1 outline-blue-500 rounded" : ""}`,
26992
27048
  style: { paddingLeft: indent },
27049
+ role: "button",
27050
+ tabIndex: 0,
26993
27051
  onClick: () => {
26994
27052
  openFolderPanel(collectionId, folder2.id);
26995
27053
  setExpanded((e) => !e);
26996
27054
  },
27055
+ onKeyDown: onActivateKey(() => {
27056
+ openFolderPanel(collectionId, folder2.id);
27057
+ setExpanded((e) => !e);
27058
+ }),
26997
27059
  onDragStart: (e) => {
26998
27060
  e.dataTransfer.effectAllowed = "move";
26999
27061
  e.stopPropagation();
@@ -27376,8 +27438,8 @@ function jsonWithComments() {
27376
27438
  function xmlWithComments() {
27377
27439
  return [xml(), commentKeymap];
27378
27440
  }
27379
- const GraphQLEditor = reactExports.lazy(() => __vitePreload(() => import("./GraphQLEditor-MImdgXb4.js"), true ? __vite__mapDeps([0,1,2]) : void 0, import.meta.url).then((m) => ({ default: m.GraphQLEditor })));
27380
- const SoapEditor = reactExports.lazy(() => __vitePreload(() => import("./SoapEditor-rxb3heue.js"), true ? __vite__mapDeps([3,1,2]) : void 0, import.meta.url).then((m) => ({ default: m.SoapEditor })));
27441
+ const GraphQLEditor = reactExports.lazy(() => __vitePreload(() => import("./GraphQLEditor-Bih8eb_r.js"), true ? __vite__mapDeps([0,1,2]) : void 0, import.meta.url).then((m) => ({ default: m.GraphQLEditor })));
27442
+ const SoapEditor = reactExports.lazy(() => __vitePreload(() => import("./SoapEditor-D1sQKu7l.js"), true ? __vite__mapDeps([3,1,2]) : void 0, import.meta.url).then((m) => ({ default: m.SoapEditor })));
27381
27443
  function BodyTab({ request: request2, onChange }) {
27382
27444
  const t = useT();
27383
27445
  const editorFallback = /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-4 text-xs text-surface-500", children: t("Loading editor…") });
@@ -27609,7 +27671,7 @@ function AuthTab({ request: request2, onChange }) {
27609
27671
  {
27610
27672
  type: "number",
27611
27673
  value: auth.oauth2RedirectPort ?? 9876,
27612
- onChange: (e) => setAuth({ oauth2RedirectPort: parseInt(e.target.value, 10) || 9876 }),
27674
+ onChange: (e) => setAuth({ oauth2RedirectPort: Number.parseInt(e.target.value, 10) || 9876 }),
27613
27675
  className: "mt-1 w-full bg-surface-800 border border-surface-700 rounded px-2 py-1 focus:outline-none focus:border-blue-500"
27614
27676
  }
27615
27677
  )
@@ -28232,17 +28294,17 @@ function renderInline(text, keyBase) {
28232
28294
  });
28233
28295
  }
28234
28296
  function renderMarkdown(md) {
28235
- const lines = md.replace(/\r\n/g, "\n").split("\n");
28297
+ const lines = md.replaceAll("\r\n", "\n").split("\n");
28236
28298
  const blocks = [];
28237
28299
  let i = 0;
28238
28300
  let key = 0;
28239
28301
  const k = () => `md-${key++}`;
28240
28302
  while (i < lines.length) {
28241
28303
  const line = lines[i];
28242
- if (/^```/.test(line.trim())) {
28304
+ if (line.trim().startsWith("```")) {
28243
28305
  const buf2 = [];
28244
28306
  i++;
28245
- while (i < lines.length && !/^```/.test(lines[i].trim())) {
28307
+ while (i < lines.length && !lines[i].trim().startsWith("```")) {
28246
28308
  buf2.push(lines[i]);
28247
28309
  i++;
28248
28310
  }
@@ -28301,7 +28363,7 @@ function renderMarkdown(md) {
28301
28363
  continue;
28302
28364
  }
28303
28365
  const buf = [];
28304
- while (i < lines.length && lines[i].trim() !== "" && !/^```/.test(lines[i].trim()) && !/^(#{1,6})\s+/.test(lines[i]) && !/^\s*>\s?/.test(lines[i]) && !/^\s*[-*+]\s+/.test(lines[i]) && !/^\s*\d+\.\s+/.test(lines[i]) && !/^\s*([-*_])\1{2,}\s*$/.test(lines[i])) {
28366
+ while (i < lines.length && lines[i].trim() !== "" && !lines[i].trim().startsWith("```") && !/^(#{1,6})\s+/.test(lines[i]) && !/^\s*>\s?/.test(lines[i]) && !/^\s*[-*+]\s+/.test(lines[i]) && !/^\s*\d+\.\s+/.test(lines[i]) && !/^\s*([-*_])\1{2,}\s*$/.test(lines[i])) {
28305
28367
  buf.push(lines[i]);
28306
28368
  i++;
28307
28369
  }
@@ -28453,7 +28515,7 @@ function useAiAvailable() {
28453
28515
  return available;
28454
28516
  }
28455
28517
  async function generateDocs(input) {
28456
- return electron$u.generateDocs({ ...input, model: getAiConfig().model });
28518
+ return electron$u.generateAiDocs({ ...input, model: getAiConfig().model });
28457
28519
  }
28458
28520
  function kvLines(label, rows) {
28459
28521
  const on = (rows ?? []).filter((r) => r.enabled !== false && r.key);
@@ -28466,8 +28528,7 @@ function requestDocsContext(req, example) {
28466
28528
  `Method: ${req.method}`,
28467
28529
  `URL: ${req.url}`
28468
28530
  ];
28469
- lines.push(...kvLines("Query params", req.params));
28470
- lines.push(...kvLines("Headers", req.headers));
28531
+ lines.push(...kvLines("Query params", req.params), ...kvLines("Headers", req.headers));
28471
28532
  if (req.auth && req.auth.type !== "none") lines.push(`Auth: ${req.auth.type}`);
28472
28533
  if (req.body && req.body.mode !== "none") {
28473
28534
  lines.push(`Request body (${req.body.mode}):`);
@@ -28475,8 +28536,7 @@ function requestDocsContext(req, example) {
28475
28536
  if (raw) lines.push(raw.slice(0, 2e3));
28476
28537
  }
28477
28538
  if (example?.response) {
28478
- lines.push(`Example response (status ${example.response.status}):`);
28479
- lines.push((example.response.body || "").slice(0, 2e3));
28539
+ lines.push(`Example response (status ${example.response.status}):`, (example.response.body || "").slice(0, 2e3));
28480
28540
  }
28481
28541
  return lines.join("\n");
28482
28542
  }
@@ -28497,8 +28557,7 @@ function collectionDocsContext(col) {
28497
28557
  const lines = [`API collection with ${reqs.length} request(s).`];
28498
28558
  const byMethod = {};
28499
28559
  for (const r of reqs) byMethod[r.method] = (byMethod[r.method] ?? 0) + 1;
28500
- lines.push("Methods: " + Object.entries(byMethod).map(([m, n]) => `${m}×${n}`).join(", "));
28501
- lines.push("Endpoints:");
28560
+ lines.push("Methods: " + Object.entries(byMethod).map(([m, n]) => `${m}×${n}`).join(", "), "Endpoints:");
28502
28561
  for (const r of reqs.slice(0, 60)) lines.push(` - ${r.method} ${r.url}`);
28503
28562
  if (reqs.length > 60) lines.push(` … and ${reqs.length - 60} more`);
28504
28563
  return lines.join("\n");
@@ -28765,7 +28824,7 @@ function textToMeta(text) {
28765
28824
  }
28766
28825
  function GrpcPanel({ request: request2, onChange }) {
28767
28826
  const t = useT();
28768
- const grpc = { ...DEFAULT_GRPC, ...request2.body.grpc ?? {} };
28827
+ const grpc = { ...DEFAULT_GRPC, ...request2.body.grpc };
28769
28828
  const grpcCalls = useStore((s) => s.grpcCalls);
28770
28829
  const setGrpcStatus = useStore((s) => s.setGrpcStatus);
28771
28830
  const addGrpcMessage = useStore((s) => s.addGrpcMessage);
@@ -29925,7 +29984,7 @@ function XmlNode({ element, depth, onLeaf }) {
29925
29984
  ] });
29926
29985
  }
29927
29986
  function esc(s) {
29928
- return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "");
29987
+ return s.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("\n", "\\n").replaceAll(/\r/g, "");
29929
29988
  }
29930
29989
  function toLit(v) {
29931
29990
  if (v === null) return "null";
@@ -29962,7 +30021,7 @@ function toJsonPathExpr(path, filterKey, filterValue) {
29962
30021
  const prefix2 = path.slice(0, arrayIdx).join(".");
29963
30022
  const arrayPart = prefix2 ? "$." + prefix2 : "$";
29964
30023
  const leafPart = path.slice(arrayIdx + 1).join(".");
29965
- const filterVal = isNaN(Number(filterValue)) ? `"${filterValue.replace(/"/g, '\\"')}"` : filterValue;
30024
+ const filterVal = Number.isNaN(Number(filterValue)) ? `"${filterValue.replaceAll('"', '\\"')}"` : filterValue;
29966
30025
  return leafPart ? `${arrayPart}[?(@.${filterKey}==${filterVal})].${leafPart}` : `${arrayPart}[?(@.${filterKey}==${filterVal})]`;
29967
30026
  }
29968
30027
  function varNameFromPath(path) {
@@ -30004,7 +30063,7 @@ function makeJsonPathSnippet(path, value2, filterKey, filterValue) {
30004
30063
  });`;
30005
30064
  }
30006
30065
  function makeXmlSnippet(selector, value2, mode) {
30007
- const sel = selector.replace(/"/g, '\\"');
30066
+ const sel = selector.replaceAll('"', '\\"');
30008
30067
  switch (mode) {
30009
30068
  case "equals":
30010
30069
  return `sp.test('${selector} equals "${esc(value2)}"', function() {
@@ -30032,7 +30091,7 @@ function makeJsonPathExtractSnippet(path, filterKey, filterValue, target) {
30032
30091
  sp.${target}.set("${varName}", String(matches[0] ?? ''));`;
30033
30092
  }
30034
30093
  function makeXmlExtractSnippet(selector, target) {
30035
- return `sp.${target}.set("extracted_value", sp.response.xmlText("${selector.replace(/"/g, '\\"')}") ?? '');`;
30094
+ return `sp.${target}.set("extracted_value", sp.response.xmlText("${selector.replaceAll('"', '\\"')}") ?? '');`;
30036
30095
  }
30037
30096
  function AssertMenu({ state, onClose, onConfirm }) {
30038
30097
  const t = useT();
@@ -30331,7 +30390,7 @@ function findPrimaryArray(data) {
30331
30390
  const { node, path, depth } = queue.shift();
30332
30391
  if (depth > 6 || node === null || typeof node !== "object") continue;
30333
30392
  if (Array.isArray(node)) {
30334
- const key = (path.split(".").pop() ?? "").replace(/\[\d+\]/g, "");
30393
+ const key = (path.split(".").pop() ?? "").replaceAll(/\[\d+\]/g, "");
30335
30394
  const objs = node.filter((x) => x && typeof x === "object" && !Array.isArray(x)).length;
30336
30395
  found.push({ path, array: node, pref: PREFERRED.test(key) ? 1 : 0, objs, depth });
30337
30396
  } else {
@@ -30715,7 +30774,7 @@ function HookResultsPanel({ results }) {
30715
30774
  "div",
30716
30775
  {
30717
30776
  className: `flex items-center gap-2 py-0.5 ${hasDetail2 ? "cursor-pointer hover:bg-surface-800/20 rounded px-1 -mx-1" : ""}`,
30718
- onClick: () => hasDetail2 && toggle(r.requestId + i),
30777
+ ...hasDetail2 ? { role: "button", tabIndex: 0, onClick: () => toggle(r.requestId + i), onKeyDown: onActivateKey(() => toggle(r.requestId + i)) } : {},
30719
30778
  children: [
30720
30779
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `w-1.5 h-1.5 rounded-full shrink-0 ${r.status === "passed" ? "bg-emerald-500" : r.status === "failed" ? "bg-red-500" : "bg-orange-500"}` }),
30721
30780
  badge && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[9px] font-bold px-1 py-0.5 rounded uppercase tracking-wide shrink-0 ${badge.cls}`, children: t(badge.label) }),
@@ -30751,7 +30810,7 @@ function extractPath(url) {
30751
30810
  try {
30752
30811
  return new URL(url).pathname || "/";
30753
30812
  } catch {
30754
- const match = url.match(/(?:https?:\/\/[^/]+)?(\/[^?]*)/);
30813
+ const match = /(?:https?:\/\/[^/]+)?(\/[^?]*)/.exec(url);
30755
30814
  return match?.[1] ?? "/";
30756
30815
  }
30757
30816
  }
@@ -31221,6 +31280,7 @@ function OverflowMenu({ button, ariaLabel, title, wrapperClassName = "", buttonC
31221
31280
  ref: menuRef,
31222
31281
  id: menuId,
31223
31282
  role: "menu",
31283
+ tabIndex: -1,
31224
31284
  "aria-label": ariaLabel,
31225
31285
  onKeyDown: onMenuKeyDown,
31226
31286
  onClick: () => setOpen(false),
@@ -31254,7 +31314,7 @@ function validateHttpSemantics(res, opts = {}) {
31254
31314
  const body = hasBody(res);
31255
31315
  const contentType = header(res.headers, "content-type");
31256
31316
  const contentEncoding = header(res.headers, "content-encoding");
31257
- const clNum = Number(header(res.headers, "content-length") ?? NaN);
31317
+ const clNum = Number(header(res.headers, "content-length") ?? Number.NaN);
31258
31318
  const isBodiless = status in BODILESS || status >= 100 && status < 200;
31259
31319
  if (isBodiless) {
31260
31320
  const declaresBody = body || Number.isFinite(clNum) && clNum > 0;
@@ -31443,7 +31503,7 @@ function ResponseViewer() {
31443
31503
  const setTabScriptTab = useStore((s) => s.setTabScriptTab);
31444
31504
  const isSending = activeTab?.isSending ?? false;
31445
31505
  const liveStream = useStore((s) => s.liveStream);
31446
- const streamForTab = liveStream && liveStream.tabId === activeTabId ? liveStream : null;
31506
+ const streamForTab = liveStream?.tabId === activeTabId ? liveStream : null;
31447
31507
  const response = activeTab?.lastResponse ?? null;
31448
31508
  const scriptResult = activeTab?.lastScriptResult ?? null;
31449
31509
  const sentRequest = activeTab?.lastSentRequest ?? null;
@@ -31670,7 +31730,7 @@ function ResponseViewer() {
31670
31730
  " KB"
31671
31731
  ] })
31672
31732
  ] }),
31673
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { role: "tablist", "aria-label": t("Response sections"), onKeyDown: onTablistKey, className: "flex items-center gap-0 min-w-0 ml-1", children: [
31733
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { role: "tablist", tabIndex: -1, "aria-label": t("Response sections"), onKeyDown: onTablistKey, className: "flex items-center gap-0 min-w-0 ml-1", children: [
31674
31734
  tabList.map((def) => renderTabButton(def, "inline")),
31675
31735
  /* @__PURE__ */ jsxRuntimeExports.jsx(
31676
31736
  OverflowMenu,
@@ -31924,7 +31984,7 @@ function ResponseViewer() {
31924
31984
  type: "item",
31925
31985
  label: t('Create variable in ":env"', { env: activeEnvName ?? "" }),
31926
31986
  onClick: () => {
31927
- setVarDialog({ name: headerMenu.key.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, ""), value: headerMenu.value });
31987
+ setVarDialog({ name: headerMenu.key.replaceAll(/[^a-zA-Z0-9]+/g, "_").replaceAll(/^_+|_+$/g, ""), value: headerMenu.value });
31928
31988
  setHeaderMenu(null);
31929
31989
  }
31930
31990
  } : { type: "header", label: t("Select an environment first") }
@@ -32333,7 +32393,7 @@ function HistoryPanel() {
32333
32393
  return out;
32334
32394
  }, [history, search]);
32335
32395
  async function downloadHar() {
32336
- const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/[:T]/g, "-");
32396
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replaceAll(/[:T]/g, "-");
32337
32397
  await electron$l.saveResults(historyToHar(history), `api-spector-history-${stamp}.har`);
32338
32398
  }
32339
32399
  function open2(entry) {
@@ -32537,7 +32597,7 @@ function WelcomeScreen() {
32537
32597
  )
32538
32598
  ] }),
32539
32599
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-400 text-sm max-w-sm", children: t("Local-first API testing with Robot Framework & Playwright code generation. Secrets stay on your machine.") }),
32540
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] mt-3", style: { color: "var(--text-muted)" }, children: t("version :version", { version: "0.6.3" }) })
32600
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] mt-3", style: { color: "var(--text-muted)" }, children: t("version :version", { version: "0.6.4" }) })
32541
32601
  ] }),
32542
32602
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-3 w-64", children: [
32543
32603
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -32807,8 +32867,7 @@ function EnvironmentEditor({ onClose }) {
32807
32867
  }
32808
32868
  function handleDelete(id) {
32809
32869
  deleteEnvironment(id);
32810
- const remaining = Object.keys(environments).filter((k) => k !== id);
32811
- setSelectedId(remaining[0] ?? "");
32870
+ setSelectedId(Object.keys(environments).find((k) => k !== id) ?? "");
32812
32871
  }
32813
32872
  function handleDuplicate(id) {
32814
32873
  duplicateEnvironment(id);
@@ -33260,7 +33319,7 @@ function EnvironmentBar({ inline = false }) {
33260
33319
  style: { color: "var(--text-primary)" },
33261
33320
  children: [
33262
33321
  /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "", children: t("No env") }),
33263
- envList.map(({ data: env2 }) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: env2.id, children: defaultEnvName && env2.name.toLowerCase() === defaultEnvName.toLowerCase() ? t(":name (default)", { name: env2.name }) : env2.name }, env2.id))
33322
+ envList.map(({ data: env2 }) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: env2.id, children: env2.name.toLowerCase() === defaultEnvName?.toLowerCase() ? t(":name (default)", { name: env2.name }) : env2.name }, env2.id))
33264
33323
  ]
33265
33324
  }
33266
33325
  ),
@@ -33542,7 +33601,7 @@ function WorkspaceSettingsModal({ onClose }) {
33542
33601
  }
33543
33602
  async function save() {
33544
33603
  const settings = {
33545
- ...workspace?.settings ?? {}
33604
+ ...workspace?.settings
33546
33605
  };
33547
33606
  if (proxyUrl.trim()) {
33548
33607
  settings.proxy = { url: proxyUrl.trim() };
@@ -33661,6 +33720,7 @@ function WorkspaceSettingsModal({ onClose }) {
33661
33720
  "input",
33662
33721
  {
33663
33722
  type: "checkbox",
33723
+ "aria-label": t("Persist request history"),
33664
33724
  checked: persistHistory,
33665
33725
  onChange: (e) => setPersistHistory(e.target.checked),
33666
33726
  className: "mt-0.5"
@@ -33796,6 +33856,7 @@ function WorkspaceSettingsModal({ onClose }) {
33796
33856
  "input",
33797
33857
  {
33798
33858
  type: "checkbox",
33859
+ "aria-label": t("Enable API Spector Cloud"),
33799
33860
  checked: cloudEnabled2,
33800
33861
  onChange: (e) => setCloudEnabled(e.target.checked),
33801
33862
  className: "mt-0.5 accent-blue-500"
@@ -34573,12 +34634,12 @@ function parseCurl(command) {
34573
34634
  } else if (hasData) {
34574
34635
  const data = dataParts.join("&");
34575
34636
  const ct = headers.find((h) => h.key.toLowerCase() === "content-type")?.value;
34576
- if (ct && ct.includes("json") || looksJson(data)) body = { mode: "json", json: data };
34637
+ if (ct?.includes("json") || looksJson(data)) body = { mode: "json", json: data };
34577
34638
  else body = { mode: "raw", raw: data, rawContentType: ct ?? "application/x-www-form-urlencoded" };
34578
34639
  }
34579
34640
  let name;
34580
34641
  try {
34581
- const u = new URL(url.replace(/\{\{[^}]+\}\}/g, "x"));
34642
+ const u = new URL(url.replaceAll(/\{\{[^}]+\}\}/g, "x"));
34582
34643
  name = `${resolvedMethod} ${u.pathname === "/" ? u.hostname : u.pathname}`;
34583
34644
  } catch {
34584
34645
  const cleaned = url.replace(/^[a-z]+:\/\//i, "").replace(/\?.*$/, "");
@@ -35427,7 +35488,7 @@ function Toolbar({ onOpenDocs: _onOpenDocs }) {
35427
35488
  ] });
35428
35489
  }
35429
35490
  function escapeHtml(s) {
35430
- return String(s ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
35491
+ return String(s ?? "").replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
35431
35492
  }
35432
35493
  function buildJsonReport(results, summary, meta = {}) {
35433
35494
  return JSON.stringify({
@@ -35743,7 +35804,7 @@ function buildJUnitReport(results, summary, meta = {}) {
35743
35804
  const label = r.iterationLabel ? ` #${r.iterationLabel}` : "";
35744
35805
  const hookPrefix = r.isHook && r.hookType ? `[${HOOK_JUNIT_LABELS[r.hookType] ?? "Hook"}] ` : "";
35745
35806
  const name = esc2(hookPrefix + r.name + label);
35746
- const classname = esc2([r.collection ?? meta.collection, ...r.scopePath ?? []].filter(Boolean).map((s) => String(s).replace(/\./g, "_")).join(".") || "API Tests");
35807
+ const classname = esc2([r.collection ?? meta.collection, ...r.scopePath ?? []].filter(Boolean).map((s) => String(s).replaceAll(".", "_")).join(".") || "API Tests");
35747
35808
  const timeSec = ((r.durationMs ?? 0) / 1e3).toFixed(3);
35748
35809
  const failures = [];
35749
35810
  if (r.status === "error") {
@@ -36328,13 +36389,13 @@ function RunnerModal() {
36328
36389
  }
36329
36390
  );
36330
36391
  }
36331
- const HTTP_METHODS$2 = ["get", "put", "post", "delete", "options", "head", "patch", "trace"];
36392
+ const HTTP_METHODS$2 = /* @__PURE__ */ new Set(["get", "put", "post", "delete", "options", "head", "patch", "trace"]);
36332
36393
  function normalizePath(url) {
36333
36394
  let u = (url || "").trim();
36334
36395
  u = u.split("#")[0].split("?")[0];
36335
- u = u.replace(/\{\{[^}]*\}\}/g, "");
36396
+ u = u.replaceAll(/\{\{[^}]*\}\}/g, "");
36336
36397
  u = u.replace(/^[a-z0-9+.-]+:\/\/[^/]*/i, "");
36337
- u = u.replace(/\/{2,}/g, "/");
36398
+ u = u.replaceAll(/\/{2,}/g, "/");
36338
36399
  if (!u.startsWith("/")) u = "/" + u;
36339
36400
  u = u.replace(/\/+$/, "");
36340
36401
  return u === "" ? "/" : u;
@@ -36354,7 +36415,7 @@ function pathMatches(requestUrl, template) {
36354
36415
  }
36355
36416
  function resolveRef$2(spec2, ref) {
36356
36417
  const parts = ref.replace(/^#\//, "").split("/");
36357
- return parts.reduce((o, k) => o?.[decodeURIComponent(k.replace(/~1/g, "/").replace(/~0/g, "~"))], spec2);
36418
+ return parts.reduce((o, k) => o?.[decodeURIComponent(k.replaceAll("~1", "/").replaceAll("~0", "~"))], spec2);
36358
36419
  }
36359
36420
  function deref$2(spec2, node, depth = 0, seen2 = /* @__PURE__ */ new Set()) {
36360
36421
  if (!node || typeof node !== "object" || depth > 8) return node;
@@ -36375,8 +36436,7 @@ function flattenSchemaPaths(schema, prefix2 = "", depth = 0) {
36375
36436
  if (type === "object" || schema.properties) {
36376
36437
  for (const [name, sub] of Object.entries(schema.properties ?? {})) {
36377
36438
  const path = prefix2 ? `${prefix2}.${name}` : name;
36378
- out.push(path);
36379
- out.push(...flattenSchemaPaths(sub, path, depth + 1));
36439
+ out.push(path, ...flattenSchemaPaths(sub, path, depth + 1));
36380
36440
  }
36381
36441
  } else if (type === "array" && schema.items) {
36382
36442
  out.push(...flattenSchemaPaths(schema.items, `${prefix2}[]`, depth + 1));
@@ -36391,14 +36451,13 @@ function flattenValuePaths(value2, prefix2 = "", depth = 0) {
36391
36451
  } else {
36392
36452
  for (const [k, v] of Object.entries(value2)) {
36393
36453
  const path = prefix2 ? `${prefix2}.${k}` : k;
36394
- out.push(path);
36395
- out.push(...flattenValuePaths(v, path, depth + 1));
36454
+ out.push(path, ...flattenValuePaths(v, path, depth + 1));
36396
36455
  }
36397
36456
  }
36398
36457
  return [...new Set(out)];
36399
36458
  }
36400
36459
  function successResponseSchema$1(spec2, responses) {
36401
- const code = Object.keys(responses ?? {}).filter((c) => /^2\d\d$/.test(c)).sort()[0];
36460
+ const code = Object.keys(responses ?? {}).filter((c) => /^2\d\d$/.test(c)).sort((a, b) => a.localeCompare(b))[0];
36402
36461
  if (!code) return void 0;
36403
36462
  const schema = responses[code]?.content?.["application/json"]?.schema ?? responses[code]?.content?.["application/json;charset=utf-8"]?.schema;
36404
36463
  return schema ? deref$2(spec2, schema) : void 0;
@@ -36409,7 +36468,7 @@ function enumerateOperations(spec2) {
36409
36468
  for (const [path, item] of Object.entries(doc.paths ?? {})) {
36410
36469
  if (!item || typeof item !== "object") continue;
36411
36470
  for (const [method, op] of Object.entries(item)) {
36412
- if (!HTTP_METHODS$2.includes(method.toLowerCase())) continue;
36471
+ if (!HTTP_METHODS$2.has(method.toLowerCase())) continue;
36413
36472
  if (!op || typeof op !== "object") continue;
36414
36473
  const declaredStatuses = Object.keys(op.responses ?? {}).filter((c) => /^\d{3}$/.test(c));
36415
36474
  ops.push({ method: method.toUpperCase(), path, operationId: op.operationId, declaredStatuses, responses: op.responses });
@@ -36470,10 +36529,10 @@ function computeCoverage(spec2, requests, observations = []) {
36470
36529
  operations: operations2
36471
36530
  };
36472
36531
  }
36473
- const HTTP_METHODS$1 = ["get", "put", "post", "delete", "options", "head", "patch", "trace"];
36532
+ const HTTP_METHODS$1 = /* @__PURE__ */ new Set(["get", "put", "post", "delete", "options", "head", "patch", "trace"]);
36474
36533
  function resolveRef$1(spec2, ref) {
36475
36534
  const parts = ref.replace(/^#\//, "").split("/");
36476
- return parts.reduce((o, k) => o?.[decodeURIComponent(k.replace(/~1/g, "/").replace(/~0/g, "~"))], spec2);
36535
+ return parts.reduce((o, k) => o?.[decodeURIComponent(k.replaceAll("~1", "/").replaceAll("~0", "~"))], spec2);
36477
36536
  }
36478
36537
  function deref$1(spec2, node, depth = 0, seen2 = /* @__PURE__ */ new Set()) {
36479
36538
  if (!node || typeof node !== "object" || depth > 8) return node;
@@ -36646,7 +36705,7 @@ function generateTests(spec2, options = {}) {
36646
36705
  for (const [path, item] of Object.entries(doc.paths ?? {})) {
36647
36706
  if (!item || typeof item !== "object") continue;
36648
36707
  for (const [method, op] of Object.entries(item)) {
36649
- if (!HTTP_METHODS$1.includes(method.toLowerCase())) continue;
36708
+ if (!HTTP_METHODS$1.has(method.toLowerCase())) continue;
36650
36709
  if (!op || typeof op !== "object") continue;
36651
36710
  if (opts.only.size && !opts.only.has(`${method.toUpperCase()} ${path}`)) continue;
36652
36711
  out.push(...generateForOperation(doc, method.toUpperCase(), path, item, op, opts));
@@ -36882,10 +36941,10 @@ function CoverageModal() {
36882
36941
  }
36883
36942
  );
36884
36943
  }
36885
- const HTTP_METHODS = ["get", "put", "post", "delete", "options", "head", "patch", "trace"];
36944
+ const HTTP_METHODS = /* @__PURE__ */ new Set(["get", "put", "post", "delete", "options", "head", "patch", "trace"]);
36886
36945
  function resolveRef(spec2, ref) {
36887
36946
  const parts = ref.replace(/^#\//, "").split("/");
36888
- return parts.reduce((o, k) => o?.[decodeURIComponent(k.replace(/~1/g, "/").replace(/~0/g, "~"))], spec2);
36947
+ return parts.reduce((o, k) => o?.[decodeURIComponent(k.replaceAll("~1", "/").replaceAll("~0", "~"))], spec2);
36889
36948
  }
36890
36949
  function deref(spec2, node, depth = 0, seen2 = /* @__PURE__ */ new Set()) {
36891
36950
  if (!node || typeof node !== "object" || depth > 8) return node;
@@ -36926,7 +36985,7 @@ function requestSchema(spec2, op) {
36926
36985
  }
36927
36986
  function successResponseSchema(spec2, op) {
36928
36987
  const responses = op?.responses ?? {};
36929
- const code = Object.keys(responses).filter((c) => /^2\d\d$/.test(c)).sort()[0];
36988
+ const code = Object.keys(responses).filter((c) => /^2\d\d$/.test(c)).sort((a, b) => a.localeCompare(b))[0];
36930
36989
  return code ? deref(spec2, responses[code])?.content?.["application/json"]?.schema : void 0;
36931
36990
  }
36932
36991
  function paramRequired(spec2, pathItem, op) {
@@ -36940,7 +36999,7 @@ function operations(spec2) {
36940
36999
  for (const [path, item] of Object.entries(spec2?.paths ?? {})) {
36941
37000
  if (!item || typeof item !== "object") continue;
36942
37001
  for (const [method, op] of Object.entries(item)) {
36943
- if (!HTTP_METHODS.includes(method.toLowerCase())) continue;
37002
+ if (!HTTP_METHODS.has(method.toLowerCase())) continue;
36944
37003
  map.set(`${method.toUpperCase()} ${path}`, { method: method.toUpperCase(), path, pathItem: item, op });
36945
37004
  }
36946
37005
  }
@@ -37144,7 +37203,7 @@ function CompareModal() {
37144
37203
  );
37145
37204
  }
37146
37205
  function toCSV(ds) {
37147
- const escape = (s) => s.includes(",") || s.includes('"') || s.includes("\n") ? `"${s.replace(/"/g, '""')}"` : s;
37206
+ const escape = (s) => s.includes(",") || s.includes('"') || s.includes("\n") ? `"${s.replaceAll('"', '""')}"` : s;
37148
37207
  return [ds.columns, ...ds.rows].map((row) => row.map(escape).join(",")).join("\n");
37149
37208
  }
37150
37209
  function parseCSV(text) {
@@ -37210,7 +37269,7 @@ function DataSetEditor({ ds, onChange, exportName, scopeLabel = "collection" })
37210
37269
  const url = URL.createObjectURL(blob);
37211
37270
  const a = document.createElement("a");
37212
37271
  a.href = url;
37213
- a.download = `${exportName.replace(/\s+/g, "_")}_data.csv`;
37272
+ a.download = `${exportName.replaceAll(/\s+/g, "_")}_data.csv`;
37214
37273
  a.click();
37215
37274
  URL.revokeObjectURL(url);
37216
37275
  }
@@ -37368,7 +37427,7 @@ function CollectionPanel() {
37368
37427
  ] }),
37369
37428
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex border-b border-surface-800 px-6 flex-shrink-0", children: [
37370
37429
  { id: "documentation", label: "Documentation", badge: col.description?.trim() ? 1 : 0 },
37371
- { id: "data", label: "Data", badge: iterCount > 0 ? iterCount : 0 },
37430
+ { id: "data", label: "Data", badge: Math.max(iterCount, 0) },
37372
37431
  { id: "variables", label: "Variables", badge: Object.keys(col.collectionVariables ?? {}).length }
37373
37432
  ].map((tab) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
37374
37433
  "button",
@@ -37454,7 +37513,7 @@ function FolderPanel() {
37454
37513
  ] }),
37455
37514
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex border-b border-surface-800 px-6 flex-shrink-0", children: [
37456
37515
  { id: "documentation", label: "Documentation", badge: folder2.description?.trim() ? 1 : 0 },
37457
- { id: "data", label: "Data", badge: iterCount > 0 ? iterCount : 0 },
37516
+ { id: "data", label: "Data", badge: Math.max(iterCount, 0) },
37458
37517
  { id: "variables", label: "Variables", badge: varRows.length }
37459
37518
  ].map((tab) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
37460
37519
  "button",
@@ -37622,7 +37681,7 @@ function MockPanel() {
37622
37681
  await electron$9.saveMock(relPath, mock);
37623
37682
  const ws = useStore.getState().workspace;
37624
37683
  if (ws) {
37625
- if (!ws.mocks) ws.mocks = [];
37684
+ ws.mocks ??= [];
37626
37685
  ws.mocks.push(relPath);
37627
37686
  await electron$9.saveWorkspace(ws);
37628
37687
  }
@@ -39117,7 +39176,7 @@ function ViolationRow({ v }) {
39117
39176
  const t = useT();
39118
39177
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1 px-4 py-2.5 border-l-2 border-red-600 bg-red-950/20 rounded-r", children: [
39119
39178
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
39120
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-mono font-bold text-red-400 uppercase tracking-wide", children: v.type.replace(/_/g, " ") }),
39179
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-mono font-bold text-red-400 uppercase tracking-wide", children: v.type.replaceAll("_", " ") }),
39121
39180
  v.path && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-mono text-surface-500 bg-surface-800 px-1.5 py-0.5 rounded", children: v.path })
39122
39181
  ] }),
39123
39182
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-red-200", children: v.message }),
@@ -39306,7 +39365,7 @@ function designContractToMock(cc, port = 4100) {
39306
39365
  id: it2.id || `route-${i}`,
39307
39366
  // Pact path templates use {id}; the mock router uses :id.
39308
39367
  method: (it2.request.method || "GET").toUpperCase(),
39309
- path: (it2.request.path || "/").replace(/\{([^}]+)\}/g, ":$1"),
39368
+ path: (it2.request.path || "/").replaceAll(/\{([^}]+)\}/g, ":$1"),
39310
39369
  statusCode: it2.response.status,
39311
39370
  headers: { "Content-Type": "application/json", ...kvToHeaders(it2.response.headers) },
39312
39371
  body: it2.response.body?.trim() ? it2.response.body : "",
@@ -39394,8 +39453,8 @@ function ContractDesignerModal({ seed, onClose }) {
39394
39453
  ...base,
39395
39454
  ...seed,
39396
39455
  id: uid(),
39397
- request: { ...base.request, ...seed.request ?? {} },
39398
- response: { ...base.response, ...seed.response ?? {} }
39456
+ request: { ...base.request, ...seed.request },
39457
+ response: { ...base.response, ...seed.response }
39399
39458
  };
39400
39459
  const contract = { ...newContract(), interactions: [interaction] };
39401
39460
  setSelectedId(contract.id);
@@ -39995,7 +40054,7 @@ function ConflictEditor({ path, onClose, onResolved }) {
39995
40054
  }, [path]);
39996
40055
  const conflictCount = reactExports.useMemo(() => parts?.filter((p) => p.type === "conflict").length ?? 0, [parts]);
39997
40056
  const unresolved = reactExports.useMemo(() => parts?.filter((p) => p.type === "conflict" && !p.choice).length ?? 0, [parts]);
39998
- const content = manual != null ? manual : parts ? assemble(parts) : "";
40057
+ const content = manual ?? (parts ? assemble(parts) : "");
39999
40058
  const stillHasMarkers = /^(<{7}|={7}|>{7})/m.test(content);
40000
40059
  function choose(idx, choice) {
40001
40060
  setParts((prev) => prev?.map((p, i) => i === idx && p.type === "conflict" ? { ...p, choice } : p) ?? prev);
@@ -40353,7 +40412,10 @@ function ChangesTab({ status, onRefresh }) {
40353
40412
  "div",
40354
40413
  {
40355
40414
  className: `flex items-center gap-2 px-3 py-1 hover:bg-surface-800/50 cursor-pointer group text-xs ${diffFile === f.path && diffStaged ? "bg-surface-800" : ""}`,
40415
+ role: "button",
40416
+ tabIndex: 0,
40356
40417
  onClick: () => showDiff(f, true),
40418
+ onKeyDown: onActivateKey(() => showDiff(f, true)),
40357
40419
  children: [
40358
40420
  /* @__PURE__ */ jsxRuntimeExports.jsx(StatusBadge, { status: f.status }),
40359
40421
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 truncate text-surface-200", children: f.path }),
@@ -40390,7 +40452,10 @@ function ChangesTab({ status, onRefresh }) {
40390
40452
  "div",
40391
40453
  {
40392
40454
  className: `flex items-center gap-2 px-3 py-1 hover:bg-surface-800/50 cursor-pointer group text-xs ${diffFile === f.path && !diffStaged ? "bg-surface-800" : ""}`,
40455
+ role: "button",
40456
+ tabIndex: 0,
40393
40457
  onClick: () => showDiff(f, false),
40458
+ onKeyDown: onActivateKey(() => showDiff(f, false)),
40394
40459
  children: [
40395
40460
  /* @__PURE__ */ jsxRuntimeExports.jsx(StatusBadge, { status: f.status }),
40396
40461
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 truncate text-surface-200", children: f.path }),
@@ -40417,7 +40482,10 @@ function ChangesTab({ status, onRefresh }) {
40417
40482
  "div",
40418
40483
  {
40419
40484
  className: `flex items-center gap-2 px-3 py-1 hover:bg-surface-800/50 cursor-pointer group text-xs ${diffFile === path && !diffStaged ? "bg-surface-800" : ""}`,
40485
+ role: "button",
40486
+ tabIndex: 0,
40420
40487
  onClick: () => showDiff({ path, status: "modified" }, false),
40488
+ onKeyDown: onActivateKey(() => showDiff({ path, status: "modified" }, false)),
40421
40489
  children: [
40422
40490
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-[11px] font-bold w-4 shrink-0 text-red-400", children: "!" }),
40423
40491
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 truncate text-red-300", children: path }),
@@ -41145,10 +41213,16 @@ function CommandPalette() {
41145
41213
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "max-h-80 overflow-y-auto", children: filtered2.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 py-6 text-center text-sm text-surface-600", children: t('No requests match ":query"', { query }) }) : filtered2.map((item, idx) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
41146
41214
  "div",
41147
41215
  {
41216
+ role: "button",
41217
+ tabIndex: 0,
41148
41218
  onClick: () => {
41149
41219
  openInTab(item.requestId, item.collectionId);
41150
41220
  setOpen(false);
41151
41221
  },
41222
+ onKeyDown: onActivateKey(() => {
41223
+ openInTab(item.requestId, item.collectionId);
41224
+ setOpen(false);
41225
+ }),
41152
41226
  onMouseEnter: () => setActiveIdx(idx),
41153
41227
  className: `flex items-center gap-3 px-4 py-2.5 cursor-pointer transition-colors ${idx === activeIdx ? "bg-surface-800" : "hover:bg-surface-800/50"}`,
41154
41228
  children: [
@@ -41307,7 +41381,10 @@ const TabRow = React$2.memo(function TabRow2({
41307
41381
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
41308
41382
  "div",
41309
41383
  {
41384
+ role: "button",
41385
+ tabIndex: 0,
41310
41386
  onClick: () => onActivate(tabId),
41387
+ onKeyDown: onActivateKey(() => onActivate(tabId)),
41311
41388
  onContextMenu: (e) => {
41312
41389
  e.preventDefault();
41313
41390
  onContextMenu(tabId, e.clientX, e.clientY);
@@ -41513,7 +41590,7 @@ function App() {
41513
41590
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#6aa3c8" }, children: "Spector" }),
41514
41591
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ml-2 text-[10px] font-normal opacity-50", children: [
41515
41592
  "v",
41516
- "0.6.3"
41593
+ "0.6.4"
41517
41594
  ] }),
41518
41595
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mx-2 opacity-30", children: "·" }),
41519
41596
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -41612,13 +41689,27 @@ function App() {
41612
41689
  /* @__PURE__ */ jsxRuntimeExports.jsx(
41613
41690
  "div",
41614
41691
  {
41615
- className: "flex-shrink-0 w-1 cursor-col-resize border-r border-surface-800 hover:border-blue-500 transition-colors",
41692
+ role: "separator",
41693
+ "aria-orientation": "vertical",
41694
+ "aria-label": "Resize sidebar",
41695
+ "aria-valuemin": 160,
41696
+ "aria-valuemax": 520,
41697
+ "aria-valuenow": sidebarWidth,
41698
+ tabIndex: 0,
41699
+ className: "flex-shrink-0 w-1 cursor-col-resize border-r border-surface-800 hover:border-blue-500 focus:border-blue-500 focus:outline-none transition-colors",
41616
41700
  onMouseDown: (e) => {
41617
41701
  dragging.current = "sidebar";
41618
41702
  dragStart.current = { x: e.clientX, w: sidebarWidth };
41619
41703
  document.body.style.cursor = "col-resize";
41620
41704
  document.body.style.userSelect = "none";
41621
41705
  e.preventDefault();
41706
+ },
41707
+ onKeyDown: (e) => {
41708
+ if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
41709
+ e.preventDefault();
41710
+ const step = e.key === "ArrowLeft" ? -16 : 16;
41711
+ setSidebarWidth((w) => Math.max(160, Math.min(520, w + step)));
41712
+ }
41622
41713
  }
41623
41714
  }
41624
41715
  )
@@ -41664,8 +41755,14 @@ function App() {
41664
41755
  /* @__PURE__ */ jsxRuntimeExports.jsx(
41665
41756
  "div",
41666
41757
  {
41758
+ role: "button",
41759
+ tabIndex: -1,
41760
+ "aria-label": t("Close menu"),
41667
41761
  className: "fixed inset-0 z-40",
41668
41762
  onClick: () => setTabContextMenu(null),
41763
+ onKeyDown: (e) => {
41764
+ if (e.key === "Escape") setTabContextMenu(null);
41765
+ },
41669
41766
  onContextMenu: (e) => {
41670
41767
  e.preventDefault();
41671
41768
  setTabContextMenu(null);
@@ -41741,7 +41838,7 @@ function App() {
41741
41838
  await electron.saveMock(relPath, newRoutes);
41742
41839
  const ws = useStore.getState().workspace;
41743
41840
  if (ws) {
41744
- if (!ws.mocks) ws.mocks = [];
41841
+ ws.mocks ??= [];
41745
41842
  ws.mocks.push(relPath);
41746
41843
  await electron.saveWorkspace(ws);
41747
41844
  }
@@ -41763,7 +41860,13 @@ function App() {
41763
41860
  /* @__PURE__ */ jsxRuntimeExports.jsx(
41764
41861
  "div",
41765
41862
  {
41766
- className: "flex-shrink-0 w-1 relative cursor-col-resize border-x border-surface-800 hover:border-blue-500 transition-colors group",
41863
+ role: "separator",
41864
+ "aria-orientation": "vertical",
41865
+ "aria-label": "Resize request pane",
41866
+ "aria-valuemin": 200,
41867
+ "aria-valuenow": requestPaneWidth ?? leftPaneRef.current?.offsetWidth ?? 400,
41868
+ tabIndex: 0,
41869
+ className: "flex-shrink-0 w-1 relative cursor-col-resize border-x border-surface-800 hover:border-blue-500 focus:border-blue-500 focus:outline-none transition-colors group",
41767
41870
  onMouseDown: (e) => {
41768
41871
  dragging.current = "request";
41769
41872
  dragStart.current = { x: e.clientX, w: leftPaneRef.current?.offsetWidth ?? requestPaneWidth ?? 400 };
@@ -41771,6 +41874,14 @@ function App() {
41771
41874
  document.body.style.userSelect = "none";
41772
41875
  e.preventDefault();
41773
41876
  },
41877
+ onKeyDown: (e) => {
41878
+ if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
41879
+ e.preventDefault();
41880
+ const step = e.key === "ArrowLeft" ? -16 : 16;
41881
+ const cur = leftPaneRef.current?.offsetWidth ?? requestPaneWidth ?? 400;
41882
+ setRequestPaneWidth(Math.max(200, Math.min(window.innerWidth - 300, cur + step)));
41883
+ }
41884
+ },
41774
41885
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(
41775
41886
  "button",
41776
41887
  {
@@ -41810,7 +41921,7 @@ if (savedTheme === "light") {
41810
41921
  }
41811
41922
  }
41812
41923
  const savedZoom = localStorage.getItem(STORAGE_KEYS.zoom);
41813
- if (savedZoom) window.electron.setZoomFactor(parseFloat(savedZoom));
41924
+ if (savedZoom) window.electron.setZoomFactor(Number.parseFloat(savedZoom));
41814
41925
  ReactDOM.createRoot(document.getElementById("root")).render(
41815
41926
  /* @__PURE__ */ jsxRuntimeExports.jsx(React$2.StrictMode, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(I18nProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorBoundary, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(App, {}) }) }) })
41816
41927
  );