@testsmith/api-spector 0.8.0 → 0.8.2

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.
@@ -1,4 +1,4 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./GraphQLEditor-DG84rEn7.js","./codemirror-CdIFBPs2.js","./ajv-CJPwFjiP.js","./SoapEditor-eFNU1uxt.js"])))=>i.map(i=>d[i]);
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./GraphQLEditor-QuR1FWgX.js","./codemirror-CdIFBPs2.js","./ajv-CJPwFjiP.js","./SoapEditor-fNRUfyvj.js"])))=>i.map(i=>d[i]);
2
2
  import { r as requireReact, g as getDefaultExportFromCjs, R as React, a as reactExports, b as autocompletion, h as hoverTooltip, j as jsxRuntimeExports, k as keymap, t as toggleComment, x as xml, c as json, d as ReactCodeMirror, o as oneDark, e as javascript } from "./codemirror-CdIFBPs2.js";
3
3
  import { r as requireAjv } from "./ajv-CJPwFjiP.js";
4
4
  var client = { exports: {} };
@@ -13113,9 +13113,10 @@ function makeTab(requestId, collectionId, opts = {}) {
13113
13113
  lastSentRequest: null,
13114
13114
  lastHookResults: null,
13115
13115
  isSending: false,
13116
- // Documentation leads and opens active, so a request presents its docs
13117
- // first. Examples have no Docs tab and fall back to Body (see RequestBuilder).
13118
- requestTab: opts.exampleId ? "body" : "docs",
13116
+ // Examples have no Docs tab and fall back to Body (see RequestBuilder).
13117
+ // Otherwise open on the workspace's configured default sub-tab (Docs unless
13118
+ // the workspace overrides it).
13119
+ requestTab: opts.exampleId ? "body" : opts.defaultTab ?? "docs",
13119
13120
  // Default to the post-response tab — that's where the typical workflow
13120
13121
  // (assertions, extracting tokens, saving variables) lives.
13121
13122
  scriptTab: "post"
@@ -13140,7 +13141,7 @@ const createTabsSlice = (set2, get2) => ({
13140
13141
  s.activeTabId = existing.id;
13141
13142
  s.activeCollectionId = collectionId;
13142
13143
  } else {
13143
- const tab = makeTab(requestId, collectionId, { protocol: protocolFor(s, requestId) });
13144
+ const tab = makeTab(requestId, collectionId, { protocol: protocolFor(s, requestId), defaultTab: s.workspace?.settings?.defaultRequestTab });
13144
13145
  s.tabs.push(tab);
13145
13146
  s.activeTabId = tab.id;
13146
13147
  s.activeCollectionId = collectionId;
@@ -13656,7 +13657,7 @@ const createCollectionsSlice = (set2, get2) => ({
13656
13657
  folder2.requestIds.push(req.id);
13657
13658
  s.activeCollectionId = collectionId;
13658
13659
  s.collections[collectionId].dirty = true;
13659
- const tab = makeTab(req.id, collectionId);
13660
+ const tab = makeTab(req.id, collectionId, { defaultTab: s.workspace?.settings?.defaultRequestTab });
13660
13661
  s.tabs.push(tab);
13661
13662
  s.activeTabId = tab.id;
13662
13663
  });
@@ -13814,7 +13815,7 @@ const createCollectionsSlice = (set2, get2) => ({
13814
13815
  folder2.requestIds.splice(idx + 1, 0, copy2.id);
13815
13816
  }
13816
13817
  s.collections[collectionId].dirty = true;
13817
- const tab = makeTab(copy2.id, collectionId, { protocol: copy2.protocol });
13818
+ const tab = makeTab(copy2.id, collectionId, { protocol: copy2.protocol, defaultTab: s.workspace?.settings?.defaultRequestTab });
13818
13819
  s.tabs.push(tab);
13819
13820
  s.activeTabId = tab.id;
13820
13821
  }),
@@ -28269,6 +28270,28 @@ function extractQueryParams(rawUrl, existing) {
28269
28270
  return { url: nextUrl, params: [...kept, ...parsed], changed: true };
28270
28271
  }
28271
28272
  const PATH_PARAM_RE = /\[([A-Za-z_]\w*)\]/g;
28273
+ function interpTokens(str, vars) {
28274
+ return str.replaceAll(/\{\{([^}]+)\}\}/g, (m, key) => {
28275
+ const k = String(key).trim();
28276
+ return k in vars ? vars[k] : m;
28277
+ });
28278
+ }
28279
+ function buildPreviewUrl(baseUrl, params, varValues = {}) {
28280
+ const templateTokens = /* @__PURE__ */ new Set();
28281
+ for (const m of baseUrl.matchAll(/\{\{([^}]+)\}\}/g)) templateTokens.add(m[1].trim());
28282
+ for (const m of baseUrl.matchAll(PATH_PARAM_RE)) templateTokens.add(m[1]);
28283
+ const enabled = (params ?? []).filter((p) => p.enabled && p.key);
28284
+ const pathRows = enabled.filter((p) => p.paramType === "path" || templateTokens.has(p.key));
28285
+ const queryRows = enabled.filter((p) => !(p.paramType === "path" || templateTokens.has(p.key)));
28286
+ const scope = { ...varValues };
28287
+ for (const p of pathRows) scope[p.key] = interpTokens(p.value, varValues);
28288
+ let url = interpTokens(baseUrl, scope);
28289
+ url = url.replaceAll(PATH_PARAM_RE, (m, name) => name in scope ? scope[name] : m);
28290
+ if (!queryRows.length) return url;
28291
+ const sep = url.includes("?") ? "&" : "?";
28292
+ const qs = queryRows.map((p) => `${encodeURIComponent(interpTokens(p.key, varValues))}=${encodeURIComponent(interpTokens(p.value, varValues))}`).join("&");
28293
+ return url + sep + qs;
28294
+ }
28272
28295
  function extractPathParamNames(url) {
28273
28296
  const names = [];
28274
28297
  for (const m of url.matchAll(PATH_PARAM_RE)) {
@@ -28427,8 +28450,8 @@ function jsonWithComments() {
28427
28450
  function xmlWithComments() {
28428
28451
  return [xml(), commentKeymap];
28429
28452
  }
28430
- const GraphQLEditor = reactExports.lazy(() => __vitePreload(() => import("./GraphQLEditor-DG84rEn7.js"), true ? __vite__mapDeps([0,1,2]) : void 0, import.meta.url).then((m) => ({ default: m.GraphQLEditor })));
28431
- const SoapEditor = reactExports.lazy(() => __vitePreload(() => import("./SoapEditor-eFNU1uxt.js"), true ? __vite__mapDeps([3,1,2]) : void 0, import.meta.url).then((m) => ({ default: m.SoapEditor })));
28453
+ const GraphQLEditor = reactExports.lazy(() => __vitePreload(() => import("./GraphQLEditor-QuR1FWgX.js"), true ? __vite__mapDeps([0,1,2]) : void 0, import.meta.url).then((m) => ({ default: m.GraphQLEditor })));
28454
+ const SoapEditor = reactExports.lazy(() => __vitePreload(() => import("./SoapEditor-fNRUfyvj.js"), true ? __vite__mapDeps([3,1,2]) : void 0, import.meta.url).then((m) => ({ default: m.SoapEditor })));
28432
28455
  function BodyTab({ request: request2, onChange }) {
28433
28456
  const t2 = useT();
28434
28457
  const editorFallback = /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-4 text-xs text-surface-500", children: t2("Loading editor…") });
@@ -31450,6 +31473,11 @@ function RequestBuilder({ request: request2 }) {
31450
31473
  const activeAppTab = useStore$1((s) => s.tabs.find((t22) => t22.id === s.activeTabId));
31451
31474
  const isSending = activeAppTab?.isSending ?? false;
31452
31475
  const activeTab = activeAppTab?.requestTab ?? "params";
31476
+ const varValues = useVarValues();
31477
+ const previewUrl = reactExports.useMemo(
31478
+ () => buildPreviewUrl(request2.url, request2.params ?? [], varValues),
31479
+ [request2.url, request2.params, varValues]
31480
+ );
31453
31481
  function setActiveTab(t22) {
31454
31482
  if (activeTabId) setTabRequestTab(activeTabId, t22);
31455
31483
  }
@@ -31956,7 +31984,22 @@ function RequestBuilder({ request: request2 }) {
31956
31984
  }
31957
31985
  )
31958
31986
  ] })
31959
- ] })
31987
+ ] }),
31988
+ !isWs && !isGrpc && !isSocketIo && !isMqtt && previewUrl && previewUrl !== request2.url && /* @__PURE__ */ jsxRuntimeExports.jsxs(
31989
+ "div",
31990
+ {
31991
+ className: "flex items-start gap-2 text-[11px] font-mono -mt-0.5 px-0.5",
31992
+ title: t2("The full request that will be sent, with the enabled params applied"),
31993
+ children: [
31994
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-500 shrink-0", children: t2("Sends") }),
31995
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "break-all", children: [
31996
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-blue-400", children: request2.method }),
31997
+ " ",
31998
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-surface-300", children: previewUrl })
31999
+ ] })
32000
+ ]
32001
+ }
32002
+ )
31960
32003
  ] }),
31961
32004
  showFuzz && /* @__PURE__ */ jsxRuntimeExports.jsx(FuzzModal, { request: request2, onClose: () => setShowFuzz(false) }),
31962
32005
  askPrompt && /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -35876,7 +35919,7 @@ function WelcomeScreen() {
35876
35919
  )
35877
35920
  ] }),
35878
35921
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-400 text-sm max-w-sm", children: t2("Local-first API testing with Robot Framework & Playwright code generation. Secrets stay on your machine.") }),
35879
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] mt-3", style: { color: "var(--text-muted)" }, children: t2("version :version", { version: "0.8.0" }) })
35922
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] mt-3", style: { color: "var(--text-muted)" }, children: t2("version :version", { version: "0.8.2" }) })
35880
35923
  ] }),
35881
35924
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-3 w-64", children: [
35882
35925
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -36853,6 +36896,7 @@ function WorkspaceSettingsModal({ onClose }) {
36853
36896
  const [defaultEnvironment, setDefaultEnvironment] = reactExports.useState(existing.defaultEnvironment ?? "");
36854
36897
  const [persistHistory, setPersistHistory] = reactExports.useState(existing.persistHistory ?? false);
36855
36898
  const [performanceTesting, setPerformanceTesting] = reactExports.useState(existing.performanceTesting ?? false);
36899
+ const [defaultRequestTab, setDefaultRequestTab] = reactExports.useState(existing.defaultRequestTab ?? "docs");
36856
36900
  const [proxyUrl, setProxyUrl] = reactExports.useState(existing.proxy?.url ?? "");
36857
36901
  const [proxyUser, setProxyUser] = reactExports.useState(existing.proxy?.auth?.username ?? "");
36858
36902
  const [proxyPass, setProxyPass] = reactExports.useState(existing.proxy?.auth?.password ?? "");
@@ -36979,6 +37023,8 @@ function WorkspaceSettingsModal({ onClose }) {
36979
37023
  else delete settings.persistHistory;
36980
37024
  if (performanceTesting) settings.performanceTesting = true;
36981
37025
  else delete settings.performanceTesting;
37026
+ if (defaultRequestTab && defaultRequestTab !== "docs") settings.defaultRequestTab = defaultRequestTab;
37027
+ else delete settings.defaultRequestTab;
36982
37028
  const secrets = {};
36983
37029
  const vault = {};
36984
37030
  if (vAddress.trim()) vault.address = vAddress.trim();
@@ -37060,6 +37106,26 @@ function WorkspaceSettingsModal({ onClose }) {
37060
37106
  )
37061
37107
  ] }),
37062
37108
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-600 text-[11px]", children: t2("CLI runs without --environment use this environment, and the app selects it when no environment is active.") }),
37109
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1 mt-2", children: [
37110
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: labelCls, children: t2("Default request tab") }),
37111
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
37112
+ "select",
37113
+ {
37114
+ value: defaultRequestTab,
37115
+ onChange: (e) => setDefaultRequestTab(e.target.value),
37116
+ className: "bg-surface-800 border border-surface-700 rounded px-2.5 py-1.5 focus:outline-none focus:border-blue-500",
37117
+ children: [
37118
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "docs", children: t2("Docs") }),
37119
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "params", children: t2("Params") }),
37120
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "headers", children: t2("Headers") }),
37121
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "body", children: t2("Body") }),
37122
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "auth", children: t2("Auth") }),
37123
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "scripts", children: t2("Scripts") })
37124
+ ]
37125
+ }
37126
+ )
37127
+ ] }),
37128
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-surface-600 text-[11px]", children: t2("Which tab a request opens on. Examples always open on Body.") }),
37063
37129
  /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-start gap-2 mt-2 cursor-pointer", children: [
37064
37130
  /* @__PURE__ */ jsxRuntimeExports.jsx(
37065
37131
  "input",
@@ -56011,7 +56077,7 @@ function App() {
56011
56077
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#6aa3c8" }, children: "Spector" }),
56012
56078
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ml-2 text-[10px] font-normal opacity-50", children: [
56013
56079
  "v",
56014
- "0.8.0"
56080
+ "0.8.2"
56015
56081
  ] }),
56016
56082
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mx-2 opacity-30", children: "·" }),
56017
56083
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -5,10 +5,10 @@
5
5
  <meta charset="UTF-8" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>API Spector</title>
8
- <script type="module" crossorigin src="./assets/index-CpSTmcc1.js"></script>
8
+ <script type="module" crossorigin src="./assets/index-CRFyHn2H.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="./assets/codemirror-CdIFBPs2.js">
10
10
  <link rel="modulepreload" crossorigin href="./assets/ajv-CJPwFjiP.js">
11
- <link rel="stylesheet" crossorigin href="./assets/index-DkhdKzQJ.css">
11
+ <link rel="stylesheet" crossorigin href="./assets/index-CB5T8Srd.css">
12
12
  </head>
13
13
 
14
14
  <body>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@testsmith/api-spector",
3
3
  "productName": "API Spector",
4
- "version": "0.8.0",
4
+ "version": "0.8.2",
5
5
  "description": "Local-first API testing tool to inspect, test and mock APIs",
6
6
  "repository": {
7
7
  "type": "git",