@testsmith/api-spector 0.6.2 → 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-Hqh3I3rs.js → request-collection-KGPuvN7o.js} +5 -4
  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 +166 -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 +50 -22
  22. package/out/main/wsdl.js +9 -9
  23. package/out/preload/index.js +1 -1
  24. package/out/renderer/assets/{GraphQLEditor-DtppmZJO.js → GraphQLEditor-Bih8eb_r.js} +6 -6
  25. package/out/renderer/assets/{SoapEditor-BwSm12wH.js → SoapEditor-D1sQKu7l.js} +3 -3
  26. package/out/renderer/assets/{index-D6-zmXen.js → index-vEDOfWlE.js} +418 -279
  27. package/out/renderer/index.html +1 -1
  28. package/package.json +1 -1
  29. package/out/main/chunks/escape-BwPM4Tf6.js +0 -9
package/out/main/index.js CHANGED
@@ -23,42 +23,38 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  ));
24
24
  const electron = require("electron");
25
25
  const handle = require("./chunks/handle-CL-nWtYA.js");
26
- const path = require("path");
27
- const fs$1 = require("fs");
28
- const promises = require("fs/promises");
29
- const crypto = require("crypto");
26
+ const path = require("node:path");
27
+ const fs = require("node:fs");
28
+ const promises = require("node:fs/promises");
29
+ const crypto = require("node:crypto");
30
30
  const JSZip = require("jszip");
31
- const dataFiles = require("./chunks/data-files-Br2h3Dt2.js");
32
- const requestExec = require("./chunks/request-exec-DAFA6Fap.js");
31
+ const dataFiles = require("./chunks/data-files-BYqhsoPe.js");
32
+ const requestExec = require("./chunks/request-exec-BRmVweue.js");
33
33
  const ipcValidate = require("./chunks/ipc-validate-sqUFqRmE.js");
34
34
  const uuid = require("uuid");
35
35
  const jsYaml = require("js-yaml");
36
36
  const undici = require("undici");
37
- const requestCollection = require("./chunks/request-collection-Hqh3I3rs.js");
38
- const mockServer = require("./chunks/mock-server-BxiXhP1m.js");
39
- const http = require("http");
37
+ const requestCollection = require("./chunks/request-collection-KGPuvN7o.js");
38
+ const mockServer = require("./chunks/mock-server-C4kTFkod.js");
39
+ const http = require("node:http");
40
40
  const WebSocket = require("ws");
41
- const fs = require("node:fs");
42
41
  const os = require("node:os");
43
- const path$1 = require("node:path");
44
42
  const grpc = require("@grpc/grpc-js");
45
43
  const protoLoader = require("@grpc/proto-loader");
46
- const soapHandler = require("./chunks/soap-handler-BAPCALkN.js");
47
- const escape = require("./chunks/escape-BwPM4Tf6.js");
48
- const promises$1 = require("node:fs/promises");
49
- const os$1 = require("os");
50
- const brokerClient = require("./chunks/broker-client-WEymdqAK.js");
44
+ const soapHandler = require("./chunks/soap-handler-BznuMFIP.js");
45
+ const escape = require("./chunks/escape-DDVAlOvZ.js");
46
+ const brokerClient = require("./chunks/broker-client-CvRuiX3o.js");
51
47
  const simpleGit = require("simple-git");
52
- const recorder = require("./chunks/recorder-0Ij921El.js");
48
+ const recorder = require("./chunks/recorder-BkO8G_mB.js");
49
+ require("fs/promises");
53
50
  require("tls");
54
- require("node:crypto");
55
51
  require("dayjs");
56
- require("vm");
52
+ require("node:vm");
57
53
  require("tv4");
58
54
  require("jsonpath-plus");
59
55
  require("@xmldom/xmldom");
60
56
  require("ajv");
61
- require("https");
57
+ require("node:https");
62
58
  function _interopNamespaceDefault(e) {
63
59
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
64
60
  if (e) {
@@ -75,9 +71,9 @@ function _interopNamespaceDefault(e) {
75
71
  n.default = e;
76
72
  return Object.freeze(n);
77
73
  }
74
+ const path__namespace = /* @__PURE__ */ _interopNamespaceDefault(path);
78
75
  const fs__namespace = /* @__PURE__ */ _interopNamespaceDefault(fs);
79
76
  const os__namespace = /* @__PURE__ */ _interopNamespaceDefault(os);
80
- const path__namespace = /* @__PURE__ */ _interopNamespaceDefault(path$1);
81
77
  const grpc__namespace = /* @__PURE__ */ _interopNamespaceDefault(grpc);
82
78
  const protoLoader__namespace = /* @__PURE__ */ _interopNamespaceDefault(protoLoader);
83
79
  const LAST_WS_FILE = path.join(electron.app.getPath("userData"), "last-workspace.json");
@@ -205,29 +201,29 @@ function readmeContents(workspaceFileName) {
205
201
  ].join("\n");
206
202
  }
207
203
  async function ensureReadme(workspaceDir2, workspaceFileName) {
208
- const path$12 = path.join(workspaceDir2, "README.md");
204
+ const path$1 = path.join(workspaceDir2, "README.md");
209
205
  try {
210
- await promises.readFile(path$12, "utf8");
206
+ await promises.readFile(path$1, "utf8");
211
207
  return;
212
208
  } catch (err) {
213
209
  if (err.code !== "ENOENT") {
214
210
  console.warn("file-handler: could not check README.md", err);
215
211
  return;
216
212
  }
217
- await atomicWrite(path$12, readmeContents(workspaceFileName));
213
+ await atomicWrite(path$1, readmeContents(workspaceFileName));
218
214
  }
219
215
  }
220
216
  async function ensureGitignore(workspaceDir2) {
221
- const path$12 = path.join(workspaceDir2, ".gitignore");
217
+ const path$1 = path.join(workspaceDir2, ".gitignore");
222
218
  try {
223
- await promises.readFile(path$12, "utf8");
219
+ await promises.readFile(path$1, "utf8");
224
220
  return;
225
221
  } catch (err) {
226
222
  if (err.code !== "ENOENT") {
227
223
  console.warn("file-handler: could not check .gitignore", err);
228
224
  return;
229
225
  }
230
- await atomicWrite(path$12, SPECTOR_GITIGNORE);
226
+ await atomicWrite(path$1, SPECTOR_GITIGNORE);
231
227
  }
232
228
  }
233
229
  async function ensureVscodeFileAssociation(workspaceDir2) {
@@ -261,7 +257,7 @@ function dialogStartDir() {
261
257
  function defaultWorkspaceName() {
262
258
  const cwd = process.env.API_SPECTOR_LAUNCH_CWD;
263
259
  if (cwd) {
264
- const base = cwd.split(/[\\/]/).filter(Boolean).pop();
260
+ const base = cwd.split(/[\\/]/).findLast(Boolean);
265
261
  if (base && /^[a-zA-Z0-9._-]+$/.test(base)) return `${base}.spector`;
266
262
  }
267
263
  return "my-workspace.spector";
@@ -477,7 +473,7 @@ Create a workspace in this folder and add "${path.basename(wsPath)}" to it? The
477
473
  });
478
474
  if (pick.canceled || pick.filePaths.length === 0) return null;
479
475
  const parent = pick.filePaths[0];
480
- const repoName = (repoUrl.match(/github\.com\/[^/]+\/([^/#?]+)/i)?.[1] ?? "repository").replace(/\.git$/i, "").replace(/[^a-zA-Z0-9._-]+/g, "-") || "repository";
476
+ const repoName = (/github\.com\/[^/]+\/([^/#?]+)/i.exec(repoUrl)?.[1] ?? "repository").replace(/\.git$/i, "").replaceAll(/[^a-zA-Z0-9._-]+/g, "-") || "repository";
481
477
  const destDir = path.join(parent, repoName);
482
478
  try {
483
479
  const existing = await promises.readdir(destDir);
@@ -541,7 +537,7 @@ async function resolveArchiveUrl(input) {
541
537
  if (url.protocol !== "https:") return null;
542
538
  if (url.pathname.toLowerCase().endsWith(".zip")) return url.toString();
543
539
  if (url.hostname === "github.com" || url.hostname === "www.github.com") {
544
- const parts = url.pathname.replace(/^\/+|\/+$/g, "").split("/");
540
+ const parts = url.pathname.replaceAll(/^\/+|\/+$/g, "").split("/");
545
541
  if (parts.length < 2) return null;
546
542
  const owner = parts[0];
547
543
  const repo = parts[1].replace(/\.git$/i, "");
@@ -630,7 +626,7 @@ async function findWorkspaceSpector(dir) {
630
626
  async function wrapCollectionAsWorkspace(collFilePath, collection) {
631
627
  const dir = path.dirname(collFilePath);
632
628
  const base = path.basename(collFilePath).replace(/\.(spector|json)$/i, "");
633
- const safe = base.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "collection";
629
+ const safe = base.replaceAll(/[^a-zA-Z0-9._-]+/g, "-").replaceAll(/^-+|-+$/g, "") || "collection";
634
630
  const collRel = `collections/${safe}.json`;
635
631
  if (!collection.id) collection.id = crypto.randomUUID();
636
632
  await promises.mkdir(path.join(dir, "collections"), { recursive: true });
@@ -707,8 +703,7 @@ function formatRequestError(err, context) {
707
703
  }
708
704
  if (stack) {
709
705
  const preview = stack.split("\n").slice(0, 6).join("\n");
710
- lines.push("[request:send] stack:");
711
- lines.push(preview);
706
+ lines.push("[request:send] stack:", preview);
712
707
  }
713
708
  return lines.join("\n");
714
709
  }
@@ -993,7 +988,7 @@ function registerAiHandlers(ipc) {
993
988
  }),
994
989
  signal: controller.signal,
995
990
  // @ts-expect-error undici accepts a dispatcher for proxy/TLS support
996
- dispatcher: await requestExec.buildDispatcher(void 0, void 0)
991
+ dispatcher: await requestExec.buildDispatcher()
997
992
  });
998
993
  if (!res.ok) {
999
994
  let detail = `HTTP ${res.status}`;
@@ -1308,7 +1303,7 @@ async function loadSpecFromUrl(url) {
1308
1303
  }
1309
1304
  function resolveRef(spec, ref) {
1310
1305
  const parts = ref.replace(/^#\//, "").split("/");
1311
- return parts.reduce((obj, key) => obj?.[decodeURIComponent(key.replace(/~1/g, "/").replace(/~0/g, "~"))], spec);
1306
+ return parts.reduce((obj, key) => obj?.[decodeURIComponent(key.replaceAll("~1", "/").replaceAll("~0", "~"))], spec);
1312
1307
  }
1313
1308
  function resolve(spec, obj, seen = /* @__PURE__ */ new Set()) {
1314
1309
  if (!obj || typeof obj !== "object") return obj;
@@ -1355,7 +1350,8 @@ function buildResponseSchema(operation, spec) {
1355
1350
  const ordered = [];
1356
1351
  if (codes.includes("200")) ordered.push("200");
1357
1352
  if (codes.includes("201")) ordered.push("201");
1358
- for (const code of codes.sort()) {
1353
+ codes.sort((a, b) => a.localeCompare(b));
1354
+ for (const code of codes) {
1359
1355
  if (/^2\d\d$/.test(code) && !ordered.includes(code)) ordered.push(code);
1360
1356
  }
1361
1357
  for (const code of codes) {
@@ -1468,7 +1464,7 @@ function buildPathParamRows(operation) {
1468
1464
  });
1469
1465
  }
1470
1466
  function rewritePathTemplate(url) {
1471
- return url.replace(/\{([^/{}]+)\}/g, (_m, name) => `{{${name}}}`);
1467
+ return url.replaceAll(/\{([^/{}]+)\}/g, (_m, name) => `{{${name}}}`);
1472
1468
  }
1473
1469
  function buildHeaders(operation) {
1474
1470
  return (operation.parameters ?? []).filter((p) => p.in === "header").map((p) => ({
@@ -1638,7 +1634,7 @@ function parseBody(body) {
1638
1634
  return { mode: "none" };
1639
1635
  }
1640
1636
  function parseAuth(auth) {
1641
- if (!auth || !auth.type || auth.type === "none") return { type: "none" };
1637
+ if (!auth?.type || auth.type === "none") return { type: "none" };
1642
1638
  if (auth.type === "bearer") {
1643
1639
  return { type: "bearer", token: auth.token ?? "" };
1644
1640
  }
@@ -1883,7 +1879,7 @@ const SPECTOR_TO_HTTP = {
1883
1879
  $isoTimestamp: "$datetime iso8601"
1884
1880
  };
1885
1881
  function mapDynamicVars(s) {
1886
- return s.replace(/\{\{\s*(\$[A-Za-z]+)[^}]*\}\}/g, (_m, name) => `{{${HTTP_TO_SPECTOR[name] ?? name}}}`);
1882
+ return s.replaceAll(/\{\{\s*(\$[A-Za-z]+)[^}]*\}\}/g, (_m, name) => `{{${HTTP_TO_SPECTOR[name] ?? name}}}`);
1887
1883
  }
1888
1884
  function contentTypeOf(headers) {
1889
1885
  return headers.find((h) => h.key.toLowerCase() === "content-type")?.value.toLowerCase() ?? "";
@@ -1921,7 +1917,7 @@ function extractAuth(headers) {
1921
1917
  }
1922
1918
  function stripScriptBlocks(bodyLines2) {
1923
1919
  const joined = bodyLines2.join("\n");
1924
- return joined.replace(/[<>]\s*\{%[\s\S]*?%\}/g, "").replace(/^\s*>\s+\S.*$/gm, "").replace(/^\s*<\s+\S.*$/gm, "").trim();
1920
+ return joined.replaceAll(/[<>]\s*\{%[\s\S]*?%\}/g, "").replaceAll(/^\s*>\s+\S.*$/gm, "").replaceAll(/^\s*<\s+\S.*$/gm, "").trim();
1925
1921
  }
1926
1922
  function splitBlocks(text) {
1927
1923
  const blocks = [];
@@ -1953,7 +1949,7 @@ function parseBlock(block) {
1953
1949
  continue;
1954
1950
  }
1955
1951
  if (t.startsWith("#") || t.startsWith("//")) continue;
1956
- if (/^@[A-Za-z0-9_]+\s*=/.test(t)) continue;
1952
+ if (/^@\w+\s*=/.test(t)) continue;
1957
1953
  break;
1958
1954
  }
1959
1955
  if (i >= lines.length) return null;
@@ -1994,7 +1990,7 @@ function parseBlock(block) {
1994
1990
  function parseHttpFile(text, name) {
1995
1991
  const collectionVariables = {};
1996
1992
  for (const line of text.split(/\r?\n/)) {
1997
- const m = /^@([A-Za-z0-9_]+)\s*=\s*(.*)$/.exec(line);
1993
+ const m = /^@(\w+)\s*=\s*(.*)$/.exec(line);
1998
1994
  if (m) collectionVariables[m[1]] = mapDynamicVars(m[2].trim());
1999
1995
  }
2000
1996
  const rootFolder = { id: uuid.v4(), name: "root", description: "", folders: [], requestIds: [] };
@@ -2084,7 +2080,7 @@ function registerImportHandlers(ipc) {
2084
2080
  throw new Error("Not a valid JSON file.");
2085
2081
  }
2086
2082
  if (Array.isArray(parsed["collections"])) {
2087
- throw new Error("This is a workspace file, not a collection. Open it with File > Open Workspace instead.");
2083
+ throw new TypeError("This is a workspace file, not a collection. Open it with File > Open Workspace instead.");
2088
2084
  }
2089
2085
  if (!parsed["rootFolder"] || !parsed["requests"]) {
2090
2086
  throw new Error("This does not look like an API Spector collection (no rootFolder / requests).");
@@ -2115,34 +2111,34 @@ function parsePostScript(script) {
2115
2111
  while (match = testBlockRegex.exec(script)) {
2116
2112
  const name = match[2];
2117
2113
  const body = match[3];
2118
- const equalMatch = body.match(/sp\.expect\(([^)]+)\)\.to(?:\.be)?\.equal\(([^)]+)\)/);
2114
+ const equalMatch = /sp\.expect\(([^)]+)\)\.to(?:\.be)?\.equal\(([^)]+)\)/.exec(body);
2119
2115
  if (equalMatch) {
2120
2116
  assertions.push({ name, accessor: equalMatch[1].trim(), kind: "equals", expected: equalMatch[2].trim() });
2121
2117
  continue;
2122
2118
  }
2123
- const includeMatch = body.match(/sp\.expect\(([^)]+)\)\.to\.(?:include|contain)\(([^)]+)\)/);
2119
+ const includeMatch = /sp\.expect\(([^)]+)\)\.to\.(?:include|contain)\(([^)]+)\)/.exec(body);
2124
2120
  if (includeMatch) {
2125
2121
  assertions.push({ name, accessor: includeMatch[1].trim(), kind: "contains", expected: includeMatch[2].trim() });
2126
2122
  continue;
2127
2123
  }
2128
- const existsMatch = body.match(/sp\.expect\(([^)]+)\)\.to\.not\.be\.oneOf\(\[null,\s*undefined\]\)/);
2124
+ const existsMatch = /sp\.expect\(([^)]+)\)\.to\.not\.be\.oneOf\(\[null,\s*undefined\]\)/.exec(body);
2129
2125
  if (existsMatch) {
2130
2126
  assertions.push({ name, accessor: existsMatch[1].trim(), kind: "exists" });
2131
2127
  continue;
2132
2128
  }
2133
- const typeMatch = body.match(/sp\.expect\(([^)]+)\)\.to\.be\.a\(\s*"([^"]+)"\s*\)/);
2129
+ const typeMatch = /sp\.expect\(([^)]+)\)\.to\.be\.a\(\s*"([^"]+)"\s*\)/.exec(body);
2134
2130
  if (typeMatch) {
2135
2131
  assertions.push({ name, accessor: typeMatch[1].trim(), kind: "type", expected: `"${typeMatch[2]}"` });
2136
2132
  continue;
2137
2133
  }
2138
- const aboveMatch = body.match(/sp\.expect\(([^)]+)\)\.to\.be\.above\((\d+)\)/);
2134
+ const aboveMatch = /sp\.expect\(([^)]+)\)\.to\.be\.above\((\d+)\)/.exec(body);
2139
2135
  if (aboveMatch) {
2140
2136
  assertions.push({ name, accessor: aboveMatch[1].trim(), kind: "above", expected: aboveMatch[2] });
2141
2137
  continue;
2142
2138
  }
2143
2139
  assertions.push({ name, accessor: "", kind: "status" });
2144
2140
  }
2145
- const statusMatch = script.match(/sp\.response\.to\.have\.status\((\d+)\)/);
2141
+ const statusMatch = /sp\.response\.to\.have\.status\((\d+)\)/.exec(script);
2146
2142
  if (statusMatch) {
2147
2143
  assertions.push({ name: `status is ${statusMatch[1]}`, accessor: "", kind: "status", expected: statusMatch[1] });
2148
2144
  }
@@ -2151,23 +2147,23 @@ function parsePostScript(script) {
2151
2147
  const target = match[1];
2152
2148
  const varName = match[2];
2153
2149
  let accessor = match[3].trim();
2154
- const stringWrap = accessor.match(/^String\((.+)\)$/);
2150
+ const stringWrap = /^String\((.+)\)$/.exec(accessor);
2155
2151
  if (stringWrap) accessor = stringWrap[1];
2156
2152
  extractions.push({ varName, accessor, target });
2157
2153
  }
2158
2154
  return { assertions, extractions };
2159
2155
  }
2160
2156
  function accessorToJsonPath(accessor) {
2161
- return accessor.replace(/^json\.?/, "").replace(/\["([^"]+)"\]/g, "['$1']");
2157
+ return accessor.replace(/^json\.?/, "").replaceAll(/\["([^"]+)"\]/g, "['$1']");
2162
2158
  }
2163
2159
  function slug(name) {
2164
- return name.replace(/\W+/g, "-").toLowerCase().replace(/^-|-$/g, "");
2160
+ return name.replaceAll(/\W+/g, "-").toLowerCase().replaceAll(/^-|-$/g, "");
2165
2161
  }
2166
2162
  function toEnvVar(key) {
2167
- return key.replace(/\W+/g, "_").toUpperCase();
2163
+ return key.replaceAll(/\W+/g, "_").toUpperCase();
2168
2164
  }
2169
2165
  function interpolateEnvVars(value, sharedVars = /* @__PURE__ */ new Set()) {
2170
- return value.replace(/\{\{([^}]+)\}\}/g, (_, key) => {
2166
+ return value.replaceAll(/\{\{([^}]+)\}\}/g, (_, key) => {
2171
2167
  const envKey = toEnvVar(key.trim());
2172
2168
  return sharedVars.has(envKey) ? `\${${envKey}}` : `\${process.env.${envKey} ?? ''}`;
2173
2169
  });
@@ -2216,7 +2212,7 @@ function buildNameMap$1(folder, requests) {
2216
2212
  return map;
2217
2213
  }
2218
2214
  function javaClass(name) {
2219
- return name.replace(/[^\w\s]/g, " ").split(/\s+/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
2215
+ return name.replaceAll(/[^\w\s]/g, " ").split(/\s+/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
2220
2216
  }
2221
2217
  function resolveEffectiveAuth(req, inherited) {
2222
2218
  return requestCollection.authIsConfigured(req.auth) ? req.auth : inherited.auth ?? req.auth;
@@ -2234,7 +2230,7 @@ function getEnvBaseUrl(environment, fallback) {
2234
2230
  }
2235
2231
  function renderTree(paths) {
2236
2232
  const root = {};
2237
- for (const p of [...paths].sort()) {
2233
+ for (const p of [...paths].sort((a, b) => a.localeCompare(b))) {
2238
2234
  let cur = root;
2239
2235
  for (const part of p.split("/")) {
2240
2236
  cur = cur[part] ??= {};
@@ -2259,16 +2255,16 @@ function restAssuredCall(method, pathArg) {
2259
2255
  }
2260
2256
  const ROBOT_REQUESTS_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
2261
2257
  function safeName(name) {
2262
- return name.replace(/[^\w\s]/g, " ").split(/\s+/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
2258
+ return name.replaceAll(/[^\w\s]/g, " ").split(/\s+/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
2263
2259
  }
2264
2260
  function robotVar(key) {
2265
- return "${" + key.replace(/\W+/g, "_").toUpperCase() + "}";
2261
+ return "${" + key.replaceAll(/\W+/g, "_").toUpperCase() + "}";
2266
2262
  }
2267
2263
  function envVar(key) {
2268
- return "%{" + key.replace(/\W+/g, "_").toUpperCase() + "}";
2264
+ return "%{" + key.replaceAll(/\W+/g, "_").toUpperCase() + "}";
2269
2265
  }
2270
2266
  function interpolate(value, vars) {
2271
- return value.replace(/\{\{([^}]+)\}\}/g, (_, key) => {
2267
+ return value.replaceAll(/\{\{([^}]+)\}\}/g, (_, key) => {
2272
2268
  const v = vars.get(key.trim());
2273
2269
  return v?.secret ? envVar(key.trim()) : robotVar(key.trim());
2274
2270
  });
@@ -2298,8 +2294,7 @@ function buildNameMap(root, requests) {
2298
2294
  function buildVariablesFile(environment) {
2299
2295
  const lines = ["*** Variables ***"];
2300
2296
  if (!environment) {
2301
- lines.push("# No environment - add your variables here");
2302
- lines.push("${BASE_URL} http://localhost:8080");
2297
+ lines.push("# No environment - add your variables here", "${BASE_URL} http://localhost:8080");
2303
2298
  return lines.join("\n") + "\n";
2304
2299
  }
2305
2300
  for (const v of environment.variables) {
@@ -2340,12 +2335,9 @@ function buildKeywordsFile(collection, varMap, nameMap, hookExtractedVars) {
2340
2335
  if (!kwName) continue;
2341
2336
  const url = interpolate(req.url, varMap);
2342
2337
  const method = req.method.charAt(0) + req.method.slice(1).toLowerCase();
2343
- lines.push(kwName);
2344
- lines.push(` [Documentation] Hook: ${req.hookType} - ${req.name}`);
2338
+ lines.push(kwName, ` [Documentation] Hook: ${req.hookType} - ${req.name}`);
2345
2339
  if (!ROBOT_REQUESTS_METHODS.includes(req.method)) {
2346
- lines.push(` Log ${req.method} is not supported by robotframework-requests - hook skipped WARN`);
2347
- lines.push(` RETURN \${None}`);
2348
- lines.push("");
2340
+ lines.push(` Log ${req.method} is not supported by robotframework-requests - hook skipped WARN`, ` RETURN \${None}`, "");
2349
2341
  continue;
2350
2342
  }
2351
2343
  const { body } = req;
@@ -2364,13 +2356,11 @@ function buildKeywordsFile(collection, varMap, nameMap, hookExtractedVars) {
2364
2356
  const parsed = parsePostScript(req.postRequestScript);
2365
2357
  for (const e of parsed.extractions) {
2366
2358
  const jp = accessorToJsonPath(e.accessor);
2367
- const rfVar = e.varName.replace(/\W+/g, "_").toUpperCase();
2359
+ const rfVar = e.varName.replaceAll(/\W+/g, "_").toUpperCase();
2368
2360
  hookExtractedVars.add(rfVar);
2369
- lines.push(` \${${rfVar}}= Evaluate str($response.json().get('${jp}', ''))`);
2370
- lines.push(` Set Suite Variable \${${rfVar}}`);
2361
+ lines.push(` \${${rfVar}}= Evaluate str($response.json().get('${jp}', ''))`, ` Set Suite Variable \${${rfVar}}`);
2371
2362
  }
2372
- lines.push(` RETURN \${response}`);
2373
- lines.push("");
2363
+ lines.push(` RETURN \${response}`, "");
2374
2364
  }
2375
2365
  for (const sub of folder.folders) processHooks(sub);
2376
2366
  }
@@ -2381,12 +2371,9 @@ function buildKeywordsFile(collection, varMap, nameMap, hookExtractedVars) {
2381
2371
  if (!req || req.disabled || req.hookType) continue;
2382
2372
  const kwName = nameMap.get(reqId);
2383
2373
  const url = interpolate(req.url, varMap);
2384
- lines.push(kwName);
2385
- lines.push(` [Documentation] ${req.description || req.name}`);
2374
+ lines.push(kwName, ` [Documentation] ${req.description || req.name}`);
2386
2375
  if (!ROBOT_REQUESTS_METHODS.includes(req.method)) {
2387
- lines.push(` Log ${req.method} is not supported by robotframework-requests - request skipped WARN`);
2388
- lines.push(` RETURN \${None}`);
2389
- lines.push("");
2376
+ lines.push(` Log ${req.method} is not supported by robotframework-requests - request skipped WARN`, ` RETURN \${None}`, "");
2390
2377
  continue;
2391
2378
  }
2392
2379
  const inherited = requestCollection.resolveInheritedAuthAndHeaders(reqId, collection);
@@ -2396,8 +2383,8 @@ function buildKeywordsFile(collection, varMap, nameMap, hookExtractedVars) {
2396
2383
  if (effectiveAuth.type === "bearer") {
2397
2384
  const token = effectiveAuth.token ?? "";
2398
2385
  if (token.includes("{{")) {
2399
- const varRef = token.match(/\{\{([^}]+)\}\}/)?.[1]?.trim();
2400
- const rfVar = varRef ? varRef.replace(/\W+/g, "_").toUpperCase() : "";
2386
+ const varRef = /\{\{([^}]+)\}\}/.exec(token)?.[1]?.trim();
2387
+ const rfVar = varRef ? varRef.replaceAll(/\W+/g, "_").toUpperCase() : "";
2401
2388
  if (rfVar && hookExtractedVars.has(rfVar)) {
2402
2389
  headerPairs.push(`Authorization=Bearer \${${rfVar}}`);
2403
2390
  } else {
@@ -2446,8 +2433,7 @@ function buildKeywordsFile(collection, varMap, nameMap, hookExtractedVars) {
2446
2433
  if (callArgs.length) {
2447
2434
  lines.push(` ... ${callArgs.join(" ")}`);
2448
2435
  }
2449
- lines.push(` RETURN \${response}`);
2450
- lines.push("");
2436
+ lines.push(` RETURN \${response}`, "");
2451
2437
  }
2452
2438
  for (const sub of folder.folders) processFolder(sub);
2453
2439
  }
@@ -2485,9 +2471,7 @@ function buildTestSuite(collection, environment, nameMap) {
2485
2471
  const req = collection.requests[reqId];
2486
2472
  if (!req || req.disabled || req.hookType) continue;
2487
2473
  const kwName = nameMap.get(reqId);
2488
- lines.push(kwName);
2489
- lines.push(` [Documentation] ${req.description || req.name}`);
2490
- lines.push(` \${response}= ${kwName}`);
2474
+ lines.push(kwName, ` [Documentation] ${req.description || req.name}`, ` \${response}= ${kwName}`);
2491
2475
  const parsed = parsePostScript(req.postRequestScript);
2492
2476
  if (parsed.assertions.length > 0) {
2493
2477
  for (const a of parsed.assertions) {
@@ -2497,15 +2481,13 @@ function buildTestSuite(collection, environment, nameMap) {
2497
2481
  lines.push(` Status Should Be ${a.expected ?? 200} \${response}`);
2498
2482
  break;
2499
2483
  case "equals": {
2500
- const expected = a.expected?.replace(/^"|"$/g, "") ?? "";
2501
- lines.push(` \${value}= Get From Dictionary \${response.json()} ${jp}`);
2502
- lines.push(` Should Be Equal As Strings \${value} ${expected}`);
2484
+ const expected = a.expected?.replaceAll(/^"|"$/g, "") ?? "";
2485
+ lines.push(` \${value}= Get From Dictionary \${response.json()} ${jp}`, ` Should Be Equal As Strings \${value} ${expected}`);
2503
2486
  break;
2504
2487
  }
2505
2488
  case "contains": {
2506
- const expected = a.expected?.replace(/^"|"$/g, "") ?? "";
2507
- lines.push(` \${value}= Get From Dictionary \${response.json()} ${jp}`);
2508
- lines.push(` Should Contain \${value} ${expected}`);
2489
+ const expected = a.expected?.replaceAll(/^"|"$/g, "") ?? "";
2490
+ lines.push(` \${value}= Get From Dictionary \${response.json()} ${jp}`, ` Should Contain \${value} ${expected}`);
2509
2491
  break;
2510
2492
  }
2511
2493
  case "exists":
@@ -2520,8 +2502,7 @@ function buildTestSuite(collection, environment, nameMap) {
2520
2502
  }
2521
2503
  for (const e of parsed.extractions) {
2522
2504
  const jp = accessorToJsonPath(e.accessor);
2523
- lines.push(` \${${e.varName}}= Get From Dictionary \${response.json()} ${jp}`);
2524
- lines.push(` Set Suite Variable \${${e.varName}}`);
2505
+ lines.push(` \${${e.varName}}= Get From Dictionary \${response.json()} ${jp}`, ` Set Suite Variable \${${e.varName}}`);
2525
2506
  }
2526
2507
  lines.push("");
2527
2508
  }
@@ -2561,7 +2542,7 @@ function generateRobotFramework(collection, environment) {
2561
2542
  (environment?.variables ?? []).map((v) => [v.key, v])
2562
2543
  );
2563
2544
  const nameMap = buildNameMap(collection.rootFolder, collection.requests);
2564
- const slug2 = collection.name.replace(/\W+/g, "_").toLowerCase();
2545
+ const slug2 = collection.name.replaceAll(/\W+/g, "_").toLowerCase();
2565
2546
  const hookExtractedVars = /* @__PURE__ */ new Set();
2566
2547
  const contentFiles = [
2567
2548
  { path: "resources/variables.resource", content: buildVariablesFile(environment) },
@@ -2628,8 +2609,7 @@ function buildHookLines$1(req, sharedVars) {
2628
2609
  lines.push(` // ${req.name}`);
2629
2610
  const parsed = parsePostScript(req.postRequestScript);
2630
2611
  if (parsed.extractions.length > 0) {
2631
- lines.push(` const hookResponse = await ${hookCall};`);
2632
- lines.push(` const hookJson = await hookResponse.json();`);
2612
+ lines.push(` const hookResponse = await ${hookCall};`, ` const hookJson = await hookResponse.json();`);
2633
2613
  for (const e of parsed.extractions) {
2634
2614
  const jsonPath = e.accessor.replace(/^json\.?/, "");
2635
2615
  const expr = jsonPath ? `hookJson.${jsonPath}` : "hookJson";
@@ -2754,8 +2734,7 @@ ${optionParts.join(",\n")},
2754
2734
  lines.push(` const json = await response.json();`);
2755
2735
  }
2756
2736
  if (req.schema?.trim()) {
2757
- lines.push(` // JSON Schema validation`);
2758
- lines.push(` // Schema: ${req.schema.replace(/\n/g, " ").slice(0, 80)}...`);
2737
+ lines.push(` // JSON Schema validation`, ` // Schema: ${req.schema.replaceAll("\n", " ").slice(0, 80)}...`);
2759
2738
  }
2760
2739
  if (parsed.assertions.length > 0) {
2761
2740
  for (const a of parsed.assertions) {
@@ -2792,8 +2771,7 @@ ${optionParts.join(",\n")},
2792
2771
  for (const e of parsed.extractions) {
2793
2772
  const path22 = e.accessor.replace(/^json\.?/, "");
2794
2773
  const expr = path22 ? `json.${path22}` : "json";
2795
- lines.push(` // Extract: ${e.varName} = ${expr}`);
2796
- lines.push(` process.env.${toEnvVar(e.varName)} = String(${expr});`);
2774
+ lines.push(` // Extract: ${e.varName} = ${expr}`, ` process.env.${toEnvVar(e.varName)} = String(${expr});`);
2797
2775
  }
2798
2776
  lines.push(` });`);
2799
2777
  tests.push(lines.join("\n"));
@@ -2808,7 +2786,7 @@ ${tests.join("\n\n")}
2808
2786
  `;
2809
2787
  }
2810
2788
  function buildPackageJson$3(collectionName) {
2811
- const name = collectionName.replace(/\W+/g, "-").toLowerCase();
2789
+ const name = collectionName.replaceAll(/\W+/g, "-").toLowerCase();
2812
2790
  return JSON.stringify({
2813
2791
  name: `${name}-api-tests`,
2814
2792
  version: "1.0.0",
@@ -2913,8 +2891,7 @@ function buildHookLines(req, sharedVars) {
2913
2891
  const lines = [` // ${req.name}`];
2914
2892
  const parsed = parsePostScript(req.postRequestScript);
2915
2893
  if (parsed.extractions.length > 0) {
2916
- lines.push(` const hookResponse = await ${hookCall};`);
2917
- lines.push(` const hookJson = await hookResponse.json();`);
2894
+ lines.push(` const hookResponse = await ${hookCall};`, ` const hookJson = await hookResponse.json();`);
2918
2895
  for (const e of parsed.extractions) {
2919
2896
  const jp = e.accessor.replace(/^json\.?/, "");
2920
2897
  const expr = jp ? `hookJson.${jp}` : "hookJson";
@@ -3082,7 +3059,7 @@ ${tests.join("\n\n")}
3082
3059
  `;
3083
3060
  }
3084
3061
  function buildPackageJson$2(collectionName) {
3085
- const name = collectionName.replace(/\W+/g, "-").toLowerCase();
3062
+ const name = collectionName.replaceAll(/\W+/g, "-").toLowerCase();
3086
3063
  return JSON.stringify({
3087
3064
  name: `${name}-api-tests`,
3088
3065
  version: "1.0.0",
@@ -3160,7 +3137,7 @@ import * as dotenv from 'dotenv'
3160
3137
  dotenv.config({ path: '.env.local' });
3161
3138
 
3162
3139
  // Add secret env vars to .env.local:
3163
- ${envComments ? `// ${envComments.replace(/\n/g, "\n// ")}
3140
+ ${envComments ? `// ${envComments.replaceAll("\n", "\n// ")}
3164
3141
  ` : ""}export const BASE_URL = process.env.BASE_URL ?? '${baseUrl}';
3165
3142
 
3166
3143
  export const api = supertest(BASE_URL);
@@ -3240,15 +3217,11 @@ ${lines.join("\n")}
3240
3217
  const enabledParams = req.params.filter((p) => p.enabled && p.key);
3241
3218
  const lines = [];
3242
3219
  if (!SUPERTEST_VERBS.includes(method)) {
3243
- lines.push(` // ${req.method} (RFC 10008) is not supported by supertest`);
3244
- lines.push(` it.skip('${nameMap.get(reqId)} [${req.method} unsupported]', () => {});`);
3245
- lines.push("");
3220
+ lines.push(` // ${req.method} (RFC 10008) is not supported by supertest`, ` it.skip('${nameMap.get(reqId)} [${req.method} unsupported]', () => {});`, "");
3246
3221
  tests.push(...lines);
3247
3222
  continue;
3248
3223
  }
3249
- lines.push(` it('${nameMap.get(reqId)}', async () => {`);
3250
- lines.push(` const res = await api`);
3251
- lines.push(` .${method}(\`${path2}\`)`);
3224
+ lines.push(` it('${nameMap.get(reqId)}', async () => {`, ` const res = await api`, ` .${method}(\`${path2}\`)`);
3252
3225
  if (effectiveAuth.type === "bearer") {
3253
3226
  const token = effectiveAuth.token ?? "";
3254
3227
  if (token.includes("{{")) {
@@ -3275,8 +3248,7 @@ ${lines.join("\n")}
3275
3248
  }
3276
3249
  } else if (req.body.mode === "form" && req.body.form) {
3277
3250
  const pairs = req.body.form.filter((p) => p.enabled && p.key).map((p) => `${p.key}: \`${interpolateEnvVars(p.value, sharedVars)}\``).join(", ");
3278
- lines.push(` .type('form')`);
3279
- lines.push(` .send({ ${pairs} })`);
3251
+ lines.push(` .type('form')`, ` .send({ ${pairs} })`);
3280
3252
  }
3281
3253
  }
3282
3254
  lines[lines.length - 1] += ";";
@@ -3326,7 +3298,7 @@ ${tests.join("\n\n")}
3326
3298
  `;
3327
3299
  }
3328
3300
  function buildPackageJson$1(collectionName) {
3329
- const name = collectionName.replace(/\W+/g, "-").toLowerCase();
3301
+ const name = collectionName.replaceAll(/\W+/g, "-").toLowerCase();
3330
3302
  return JSON.stringify({
3331
3303
  name: `${name}-api-tests`,
3332
3304
  version: "1.0.0",
@@ -3424,7 +3396,7 @@ function buildClient(environment) {
3424
3396
  require('dotenv').config({ path: '.env.local' });
3425
3397
 
3426
3398
  // Add secret env vars to .env.local:
3427
- ${envComments ? `// ${envComments.replace(/\n/g, "\n// ")}
3399
+ ${envComments ? `// ${envComments.replaceAll("\n", "\n// ")}
3428
3400
  ` : ""}const BASE_URL = process.env.BASE_URL ?? '${baseUrl}';
3429
3401
 
3430
3402
  module.exports.api = supertest(BASE_URL);
@@ -3504,15 +3476,11 @@ ${lines.join("\n")}
3504
3476
  const enabledParams = req.params.filter((p) => p.enabled && p.key);
3505
3477
  const lines = [];
3506
3478
  if (!SUPERTEST_VERBS.includes(method)) {
3507
- lines.push(` // ${req.method} (RFC 10008) is not supported by supertest`);
3508
- lines.push(` it.skip('${nameMap.get(reqId)} [${req.method} unsupported]', () => {});`);
3509
- lines.push("");
3479
+ lines.push(` // ${req.method} (RFC 10008) is not supported by supertest`, ` it.skip('${nameMap.get(reqId)} [${req.method} unsupported]', () => {});`, "");
3510
3480
  tests.push(...lines);
3511
3481
  continue;
3512
3482
  }
3513
- lines.push(` it('${nameMap.get(reqId)}', async () => {`);
3514
- lines.push(` const res = await api`);
3515
- lines.push(` .${method}(\`${path2}\`)`);
3483
+ lines.push(` it('${nameMap.get(reqId)}', async () => {`, ` const res = await api`, ` .${method}(\`${path2}\`)`);
3516
3484
  if (effectiveAuth.type === "bearer") {
3517
3485
  const token = effectiveAuth.token ?? "";
3518
3486
  if (token.includes("{{")) {
@@ -3539,8 +3507,7 @@ ${lines.join("\n")}
3539
3507
  }
3540
3508
  } else if (req.body.mode === "form" && req.body.form) {
3541
3509
  const pairs = req.body.form.filter((p) => p.enabled && p.key).map((p) => `${p.key}: \`${interpolateEnvVars(p.value, sharedVars)}\``).join(", ");
3542
- lines.push(` .type('form')`);
3543
- lines.push(` .send({ ${pairs} })`);
3510
+ lines.push(` .type('form')`, ` .send({ ${pairs} })`);
3544
3511
  }
3545
3512
  }
3546
3513
  lines[lines.length - 1] += ";";
@@ -3590,7 +3557,7 @@ ${tests.join("\n\n")}
3590
3557
  `;
3591
3558
  }
3592
3559
  function buildPackageJson(collectionName) {
3593
- const name = collectionName.replace(/\W+/g, "-").toLowerCase();
3560
+ const name = collectionName.replaceAll(/\W+/g, "-").toLowerCase();
3594
3561
  return JSON.stringify({
3595
3562
  name: `${name}-api-tests`,
3596
3563
  version: "1.0.0",
@@ -3653,11 +3620,11 @@ function generateSupertestJs(collection, environment) {
3653
3620
  return files;
3654
3621
  }
3655
3622
  function javaMethod(name) {
3656
- const parts = name.replace(/[^\w\s]/g, " ").split(/\s+/).filter(Boolean);
3623
+ const parts = name.replaceAll(/[^\w\s]/g, " ").split(/\s+/).filter(Boolean);
3657
3624
  return parts[0].toLowerCase() + parts.slice(1).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
3658
3625
  }
3659
3626
  function javaTypeFor(expected) {
3660
- switch (expected?.replace(/"/g, "")) {
3627
+ switch (expected?.replaceAll('"', "")) {
3661
3628
  case "string":
3662
3629
  return "String.class";
3663
3630
  case "number":
@@ -3669,7 +3636,7 @@ function javaTypeFor(expected) {
3669
3636
  }
3670
3637
  }
3671
3638
  function interpolateJava(value, sharedVars = /* @__PURE__ */ new Set()) {
3672
- return '"' + value.replace(/\{\{([^}]+)\}\}/g, (_, key) => {
3639
+ return '"' + value.replaceAll(/\{\{([^}]+)\}\}/g, (_, key) => {
3673
3640
  const envKey = toEnvVar(key.trim());
3674
3641
  if (sharedVars.has(envKey)) {
3675
3642
  return `" + ${envKey} + "`;
@@ -3678,7 +3645,7 @@ function interpolateJava(value, sharedVars = /* @__PURE__ */ new Set()) {
3678
3645
  }) + '"';
3679
3646
  }
3680
3647
  function buildPom$1(collectionName) {
3681
- const artifact = collectionName.replace(/\W+/g, "-").toLowerCase();
3648
+ const artifact = collectionName.replaceAll(/\W+/g, "-").toLowerCase();
3682
3649
  return `<?xml version="1.0" encoding="UTF-8"?>
3683
3650
  <project xmlns="http://maven.apache.org/POM/4.0.0"
3684
3651
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -3801,7 +3768,7 @@ function buildTestClass(folderName, folder, collection) {
3801
3768
  if (parsed.extractions.length > 0) {
3802
3769
  lines.push(` var hookResponse = given().spec(requestSpec)`);
3803
3770
  if (h.body.mode === "json" && h.body.json) {
3804
- const escaped = h.body.json.replace(/"/g, '\\"').replace(/\n/g, "\\n");
3771
+ const escaped = h.body.json.replaceAll('"', '\\"').replaceAll("\n", "\\n");
3805
3772
  lines.push(` .body("${escaped}")`);
3806
3773
  }
3807
3774
  lines.push(` .when()${restAssuredCall(method, `"${path2}"`)};`);
@@ -3857,14 +3824,11 @@ ${lines.join("\n")}
3857
3824
  const allHeaders = mergeHeaders(req, inherited);
3858
3825
  const enabledParams = req.params.filter((p) => p.enabled && p.key);
3859
3826
  const lines = [];
3860
- lines.push(` @Test`);
3861
- lines.push(` public void ${methodName}() {`);
3862
- lines.push(` given()`);
3863
- lines.push(` .spec(requestSpec)`);
3827
+ lines.push(` @Test`, ` public void ${methodName}() {`, ` given()`, ` .spec(requestSpec)`);
3864
3828
  if (effectiveAuth.type === "bearer") {
3865
3829
  const token = effectiveAuth.token ?? "";
3866
3830
  if (token.includes("{{")) {
3867
- const varRef = token.match(/\{\{([^}]+)\}\}/)?.[1]?.trim();
3831
+ const varRef = /\{\{([^}]+)\}\}/.exec(token)?.[1]?.trim();
3868
3832
  const envKey = varRef ? toEnvVar(varRef) : "";
3869
3833
  if (envKey && sharedVars.has(envKey)) {
3870
3834
  lines.push(` .header("Authorization", "Bearer " + ${envKey})`);
@@ -3883,13 +3847,11 @@ ${lines.join("\n")}
3883
3847
  }
3884
3848
  if (hasBody(req)) {
3885
3849
  if (req.body.mode === "json" && req.body.json) {
3886
- const escaped = req.body.json.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
3850
+ const escaped = req.body.json.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("\n", "\\n");
3887
3851
  lines.push(` .body(${interpolateJava(escaped, sharedVars)})`);
3888
3852
  }
3889
3853
  }
3890
- lines.push(` .when()`);
3891
- lines.push(` ${restAssuredCall(method, javaPath)}`);
3892
- lines.push(` .then()`);
3854
+ lines.push(` .when()`, ` ${restAssuredCall(method, javaPath)}`, ` .then()`);
3893
3855
  const parsed = parsePostScript(req.postRequestScript);
3894
3856
  if (parsed.assertions.length > 0) {
3895
3857
  for (const a of parsed.assertions) {
@@ -3899,11 +3861,7 @@ ${lines.join("\n")}
3899
3861
  lines.push(` .statusCode(${a.expected ?? 200})`);
3900
3862
  break;
3901
3863
  case "equals":
3902
- if (a.expected?.startsWith('"')) {
3903
- lines.push(` .body("${jp}", equalTo(${a.expected}))`);
3904
- } else {
3905
- lines.push(` .body("${jp}", equalTo(${a.expected}))`);
3906
- }
3864
+ lines.push(` .body("${jp}", equalTo(${a.expected}))`);
3907
3865
  break;
3908
3866
  case "contains":
3909
3867
  lines.push(` .body("${jp}", containsString(${a.expected}))`);
@@ -3992,19 +3950,19 @@ function generateRestAssured(collection, environment) {
3992
3950
  return files;
3993
3951
  }
3994
3952
  function jsVar(name) {
3995
- const parts = name.replace(/[^a-zA-Z0-9]+/g, " ").split(/\s+/).filter(Boolean).map((p) => p.toLowerCase());
3953
+ const parts = name.replaceAll(/[^a-zA-Z0-9]+/g, " ").split(/\s+/).filter(Boolean).map((p) => p.toLowerCase());
3996
3954
  if (parts.length === 0) return "_";
3997
3955
  return parts[0] + parts.slice(1).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
3998
3956
  }
3999
3957
  function featureFileName(name) {
4000
- const slug2 = name.replace(/[^\w\s-]/g, "").trim().replace(/\s+/g, "-").toLowerCase();
3958
+ const slug2 = name.replaceAll(/[^\w\s-]/g, "").trim().replaceAll(/\s+/g, "-").toLowerCase();
4001
3959
  return slug2 || "tests";
4002
3960
  }
4003
3961
  function configKey(envKey) {
4004
3962
  return jsVar(envKey);
4005
3963
  }
4006
3964
  function escSingle(s) {
4007
- return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
3965
+ return s.replaceAll("\\", "\\\\").replaceAll("'", "\\'");
4008
3966
  }
4009
3967
  function interpolateKarate(value) {
4010
3968
  if (!value.includes("{{")) return `'${escSingle(value)}'`;
@@ -4021,7 +3979,7 @@ function interpolateKarate(value) {
4021
3979
  return parts.length === 1 ? parts[0] : parts.join(" + ");
4022
3980
  }
4023
3981
  function urlSteps(url) {
4024
- const leadingVar = url.match(/^\{\{([^}]+)\}\}(.*)$/);
3982
+ const leadingVar = /^\{\{([^}]+)\}\}(.*)$/.exec(url);
4025
3983
  if (leadingVar) {
4026
3984
  const baseVar = configKey(leadingVar[1].trim());
4027
3985
  const rest2 = leadingVar[2].replace(/^\//, "");
@@ -4038,7 +3996,7 @@ function urlSteps(url) {
4038
3996
  return steps;
4039
3997
  }
4040
3998
  function buildPom(collectionName) {
4041
- const artifact = collectionName.replace(/\W+/g, "-").toLowerCase();
3999
+ const artifact = collectionName.replaceAll(/\W+/g, "-").toLowerCase();
4042
4000
  return `<?xml version="1.0" encoding="UTF-8"?>
4043
4001
  <project xmlns="http://maven.apache.org/POM/4.0.0"
4044
4002
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -4090,12 +4048,7 @@ function buildPom(collectionName) {
4090
4048
  function buildKarateConfig(environment) {
4091
4049
  const baseUrl = getEnvBaseUrl(environment, "http://localhost:8080");
4092
4050
  const lines = [];
4093
- lines.push(`function fn() {`);
4094
- lines.push(` var env = karate.env || 'dev';`);
4095
- lines.push(` karate.log('karate env:', env);`);
4096
- lines.push(``);
4097
- lines.push(` var config = {`);
4098
- lines.push(` baseUrl: '${escSingle(baseUrl)}'`);
4051
+ lines.push(`function fn() {`, ` var env = karate.env || 'dev';`, ` karate.log('karate env:', env);`, ``, ` var config = {`, ` baseUrl: '${escSingle(baseUrl)}'`);
4099
4052
  const otherVars = (environment?.variables ?? []).filter((v) => {
4100
4053
  const k = v.key.toLowerCase();
4101
4054
  return v.enabled && !["base_url", "baseurl", "base-url"].includes(k);
@@ -4103,24 +4056,13 @@ function buildKarateConfig(environment) {
4103
4056
  for (const v of otherVars) {
4104
4057
  const key = configKey(v.key);
4105
4058
  const def = v.secret ? "''" : `'${escSingle(v.value ?? "")}'`;
4106
- lines.push(`,`);
4107
- lines.push(` ${key}: ${def}`);
4108
- }
4109
- lines.push(` };`);
4110
- lines.push(``);
4111
- lines.push(` // Allow each variable to be overridden via a process env var of the same`);
4112
- lines.push(` // SHOUTY_SNAKE_CASE name (e.g. AUTH_TOKEN populates config.authToken).`);
4113
- lines.push(` function envOverride(name, key) {`);
4114
- lines.push(` var v = java.lang.System.getenv(name);`);
4115
- lines.push(` if (v) config[key] = v;`);
4116
- lines.push(` }`);
4117
- lines.push(` envOverride('BASE_URL', 'baseUrl');`);
4059
+ lines.push(`,`, ` ${key}: ${def}`);
4060
+ }
4061
+ lines.push(` };`, ``, ` // Allow each variable to be overridden via a process env var of the same`, ` // SHOUTY_SNAKE_CASE name (e.g. AUTH_TOKEN populates config.authToken).`, ` function envOverride(name, key) {`, ` var v = java.lang.System.getenv(name);`, ` if (v) config[key] = v;`, ` }`, ` envOverride('BASE_URL', 'baseUrl');`);
4118
4062
  for (const v of otherVars) {
4119
4063
  lines.push(` envOverride('${v.key}', '${configKey(v.key)}');`);
4120
4064
  }
4121
- lines.push(``);
4122
- lines.push(` return config;`);
4123
- lines.push(`}`);
4065
+ lines.push(``, ` return config;`, `}`);
4124
4066
  return lines.join("\n") + "\n";
4125
4067
  }
4126
4068
  function buildRunner(collectionName) {
@@ -4179,7 +4121,7 @@ function buildBackground(folderId, collection) {
4179
4121
  return { lines, defined };
4180
4122
  }
4181
4123
  function bodyDocstring(json) {
4182
- const expanded = json.replace(/\{\{([^}]+)\}\}/g, (_, k) => `#(${configKey(k.trim())})`);
4124
+ const expanded = json.replaceAll(/\{\{([^}]+)\}\}/g, (_, k) => `#(${configKey(k.trim())})`);
4183
4125
  let pretty = expanded.trim();
4184
4126
  try {
4185
4127
  pretty = JSON.stringify(JSON.parse(expanded), null, 2);
@@ -4191,9 +4133,9 @@ function bodyDocstring(json) {
4191
4133
  return out;
4192
4134
  }
4193
4135
  function rawDocstring(text) {
4194
- const expanded = text.replace(/\{\{([^}]+)\}\}/g, (_, k) => `#(${configKey(k.trim())})`);
4136
+ const expanded = text.replaceAll(/\{\{([^}]+)\}\}/g, (_, k) => `#(${configKey(k.trim())})`);
4195
4137
  const out = [' """'];
4196
- for (const l of expanded.replace(/\r\n/g, "\n").split("\n")) out.push(` ${l}`);
4138
+ for (const l of expanded.replaceAll("\r\n", "\n").split("\n")) out.push(` ${l}`);
4197
4139
  out.push(' """');
4198
4140
  return out;
4199
4141
  }
@@ -4254,13 +4196,12 @@ function buildFeature(folderName, folder, collection) {
4254
4196
  const enabledParams = req.params.filter((p) => p.enabled && p.key);
4255
4197
  const method = req.method.toLowerCase();
4256
4198
  const lines = [];
4257
- lines.push(`@${tag}`);
4258
- lines.push(`Scenario: ${req.name}`);
4199
+ lines.push(`@${tag}`, `Scenario: ${req.name}`);
4259
4200
  const setup = [...urlSteps(req.url)];
4260
4201
  if (effectiveAuth.type === "bearer") {
4261
4202
  const token = effectiveAuth.token ?? "";
4262
4203
  if (token.includes("{{")) {
4263
- const single = token.match(/^\{\{([^}]+)\}\}$/);
4204
+ const single = /^\{\{([^}]+)\}\}$/.exec(token);
4264
4205
  if (single) {
4265
4206
  setup.push([`header Authorization = 'Bearer ' + ${configKey(single[1].trim())}`]);
4266
4207
  } else {
@@ -4315,7 +4256,7 @@ function buildFeature(folderName, folder, collection) {
4315
4256
  asserts.push(`match ${target} != null`);
4316
4257
  break;
4317
4258
  case "type": {
4318
- const t = (a.expected ?? "").replace(/"/g, "");
4259
+ const t = (a.expected ?? "").replaceAll('"', "");
4319
4260
  const fuzzy = ["string", "number", "boolean", "array", "object"].includes(t) ? `'#${t}'` : `'#notnull'`;
4320
4261
  asserts.push(`match ${target} == ${fuzzy}`);
4321
4262
  break;
@@ -4403,7 +4344,7 @@ function generateKarate(collection, environment) {
4403
4344
  return files;
4404
4345
  }
4405
4346
  function mapOut$1(s) {
4406
- return s.replace(/\{\{\s*(\$[A-Za-z]+)\s*\}\}/g, (_m, name) => `{{${SPECTOR_TO_HTTP[name] ?? name}}}`);
4347
+ return s.replaceAll(/\{\{\s*(\$[A-Za-z]+)\s*\}\}/g, (_m, name) => `{{${SPECTOR_TO_HTTP[name] ?? name}}}`);
4407
4348
  }
4408
4349
  function orderedRequests$1(collection) {
4409
4350
  const out = [];
@@ -4493,21 +4434,20 @@ function generateHttpFile(collection, environment) {
4493
4434
  }
4494
4435
  for (const h of headers) lines.push(`${h.key}: ${mapOut$1(h.value)}`);
4495
4436
  if (body.text) {
4496
- lines.push("");
4497
- lines.push(mapOut$1(body.text));
4437
+ lines.push("", mapOut$1(body.text));
4498
4438
  }
4499
4439
  });
4500
- const slug2 = collection.name.replace(/\W+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() || "collection";
4440
+ const slug2 = collection.name.replaceAll(/\W+/g, "-").replaceAll(/^-+|-+$/g, "").toLowerCase() || "collection";
4501
4441
  return [{ path: `${slug2}.http`, content: lines.join("\n") + "\n" }];
4502
4442
  }
4503
4443
  function mapOut(s) {
4504
- return s.replace(/\{\{\s*([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g, (_m, name) => `\${${name}}`);
4444
+ return s.replaceAll(/\{\{\s*([A-Za-z_]\w*)\s*\}\}/g, (_m, name) => `\${${name}}`);
4505
4445
  }
4506
4446
  function shq(s) {
4507
- return `'${s.replace(/'/g, `'\\''`)}'`;
4447
+ return `'${s.replaceAll("'", `'\\''`)}'`;
4508
4448
  }
4509
4449
  function dq(s) {
4510
- return `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
4450
+ return `"${s.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
4511
4451
  }
4512
4452
  function orderedRequests(collection) {
4513
4453
  const out = [];
@@ -4605,7 +4545,7 @@ function generateCurl(collection, environment) {
4605
4545
  if (body.text) args.push(`--data ${dq(mapOut(body.text))}`);
4606
4546
  lines.push(args.join(" \\\n "));
4607
4547
  });
4608
- const slug2 = collection.name.replace(/\W+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() || "collection";
4548
+ const slug2 = collection.name.replaceAll(/\W+/g, "-").replaceAll(/^-+|-+$/g, "").toLowerCase() || "collection";
4609
4549
  return [{ path: `${slug2}.sh`, content: lines.join("\n") + "\n" }];
4610
4550
  }
4611
4551
  function registerGenerateHandlers(ipc) {
@@ -4642,8 +4582,8 @@ function registerGenerateHandlers(ipc) {
4642
4582
  }
4643
4583
  });
4644
4584
  handle.handleIpc(ipc, handle.IPC.generate.saveZip, async (_e, files, collectionName, target) => {
4645
- const colSlug = collectionName.replace(/\W+/g, "-").toLowerCase();
4646
- const targetSlug = target.replace(/_/g, "-");
4585
+ const colSlug = collectionName.replaceAll(/\W+/g, "-").toLowerCase();
4586
+ const targetSlug = target.replaceAll("_", "-");
4647
4587
  const defaultName = `${colSlug}-${targetSlug}.zip`;
4648
4588
  const { canceled, filePath } = await electron.dialog.showSaveDialog({
4649
4589
  title: "Save as ZIP",
@@ -4686,6 +4626,7 @@ function registerRunnerHandler(ipc) {
4686
4626
  isHook: item.isHook,
4687
4627
  hookType: item.hookType,
4688
4628
  scopeId: item.scopeId,
4629
+ collection: item.collection,
4689
4630
  scopePath: item.scopePath,
4690
4631
  iterationLabel: item.iterationLabel
4691
4632
  };
@@ -4699,6 +4640,7 @@ function registerRunnerHandler(ipc) {
4699
4640
  isHook: item.isHook,
4700
4641
  hookType: item.hookType,
4701
4642
  scopeId: item.scopeId,
4643
+ collection: item.collection,
4702
4644
  scopePath: item.scopePath
4703
4645
  };
4704
4646
  event.sender.send(handle.IPC.runner.progress, { requestId: item.request.id, ...runningUpdate });
@@ -4707,7 +4649,7 @@ function registerRunnerHandler(ipc) {
4707
4649
  collectionVars: { ...item.collectionVars, ...runCollectionVars },
4708
4650
  envVars: runEnvVars,
4709
4651
  globals: runGlobals,
4710
- localVars: { ...runLocalVars, ...item.dataRow ?? {} },
4652
+ localVars: { ...runLocalVars, ...item.dataRow },
4711
4653
  dispatcher,
4712
4654
  piiMaskPatterns,
4713
4655
  proxy,
@@ -4730,6 +4672,7 @@ function registerRunnerHandler(ipc) {
4730
4672
  isHook: item.isHook,
4731
4673
  hookType: item.hookType,
4732
4674
  scopeId: item.scopeId,
4675
+ collection: item.collection,
4733
4676
  scopePath: item.scopePath
4734
4677
  });
4735
4678
  if (requestDelay > 0 && item !== items[items.length - 1]) {
@@ -5021,7 +4964,7 @@ function registerWsHandlers(ipc) {
5021
4964
  });
5022
4965
  handle.handleIpc(ipc, handle.IPC.ws.send, async (_event, requestId, data) => {
5023
4966
  const ws = connections.get(requestId);
5024
- if (!ws || ws.readyState !== WebSocket.OPEN) {
4967
+ if (ws?.readyState !== WebSocket.OPEN) {
5025
4968
  throw new Error("WebSocket is not connected");
5026
4969
  }
5027
4970
  ws.send(data);
@@ -5056,7 +4999,7 @@ function resolveProtoFile(src) {
5056
4999
  if (src.protoPath) {
5057
5000
  return { file: src.protoPath, includeDirs: [path__namespace.dirname(src.protoPath), ...importPaths] };
5058
5001
  }
5059
- if (src.protoSource && src.protoSource.trim()) {
5002
+ if (src.protoSource?.trim()) {
5060
5003
  const dir = fs__namespace.mkdtempSync(path__namespace.join(os__namespace.tmpdir(), "apispector-proto-"));
5061
5004
  const file = path__namespace.join(dir, "main.proto");
5062
5005
  fs__namespace.writeFileSync(file, src.protoSource, "utf8");
@@ -5202,28 +5145,19 @@ function langForContentType(ct) {
5202
5145
  return "";
5203
5146
  }
5204
5147
  function escMd(s) {
5205
- return s.replace(/[|\\`*_{}[\]()#+\-.!]/g, (c) => `\\${c}`);
5148
+ return s.replaceAll(/[|\\`*_{}[\]()#+\-.!]/g, (c) => `\\${c}`);
5206
5149
  }
5207
5150
  function requestToMarkdown(req, example) {
5208
5151
  const lines = [];
5209
5152
  const methodLabel = req.protocol === "websocket" ? "WS" : req.method;
5210
- lines.push(`#### ${methodLabel} ${escMd(req.name)}`);
5211
- lines.push("");
5153
+ lines.push(`#### ${methodLabel} ${escMd(req.name)}`, "");
5212
5154
  if (req.description?.trim()) {
5213
- lines.push(req.description.trim());
5214
- lines.push("");
5155
+ lines.push(req.description.trim(), "");
5215
5156
  }
5216
- lines.push("**URL**");
5217
- lines.push("```");
5218
- lines.push(req.url || "(no url)");
5219
- lines.push("```");
5220
- lines.push("");
5157
+ lines.push("**URL**", "```", req.url || "(no url)", "```", "");
5221
5158
  const enabledParams = req.params.filter((p) => p.enabled && p.key);
5222
5159
  if (enabledParams.length) {
5223
- lines.push("**Query Parameters**");
5224
- lines.push("");
5225
- lines.push("| Key | Value | Description |");
5226
- lines.push("|-----|-------|-------------|");
5160
+ lines.push("**Query Parameters**", "", "| Key | Value | Description |", "|-----|-------|-------------|");
5227
5161
  for (const p of enabledParams) {
5228
5162
  lines.push(`| ${escMd(p.key)} | ${escMd(p.value)} | ${escMd(p.description ?? "")} |`);
5229
5163
  }
@@ -5231,63 +5165,37 @@ function requestToMarkdown(req, example) {
5231
5165
  }
5232
5166
  const enabledHeaders = req.headers.filter((h) => h.enabled && h.key);
5233
5167
  if (enabledHeaders.length) {
5234
- lines.push("**Headers**");
5235
- lines.push("");
5236
- lines.push("| Key | Value |");
5237
- lines.push("|-----|-------|");
5168
+ lines.push("**Headers**", "", "| Key | Value |", "|-----|-------|");
5238
5169
  for (const h of enabledHeaders) {
5239
5170
  lines.push(`| ${escMd(h.key)} | ${escMd(h.value)} |`);
5240
5171
  }
5241
5172
  lines.push("");
5242
5173
  }
5243
5174
  if (req.auth.type !== "none") {
5244
- lines.push(`**Auth**: ${req.auth.type}`);
5245
- lines.push("");
5175
+ lines.push(`**Auth**: ${req.auth.type}`, "");
5246
5176
  }
5247
5177
  const mode = req.body.mode;
5248
5178
  if (mode === "json" && req.body.json?.trim()) {
5249
- lines.push("**Body** (JSON)");
5250
- lines.push("```json");
5251
- lines.push(req.body.json.trim());
5252
- lines.push("```");
5253
- lines.push("");
5179
+ lines.push("**Body** (JSON)", "```json", req.body.json.trim(), "```", "");
5254
5180
  } else if (mode === "raw" && req.body.raw?.trim()) {
5255
5181
  const ct = req.body.rawContentType ?? "text";
5256
- lines.push(`**Body** (${ct})`);
5257
- lines.push("```");
5258
- lines.push(req.body.raw.trim());
5259
- lines.push("```");
5260
- lines.push("");
5182
+ lines.push(`**Body** (${ct})`, "```", req.body.raw.trim(), "```", "");
5261
5183
  } else if (mode === "graphql" && req.body.graphql?.query?.trim()) {
5262
- lines.push("**Body** (GraphQL)");
5263
- lines.push("```graphql");
5264
- lines.push(req.body.graphql.query.trim());
5265
- lines.push("```");
5266
- lines.push("");
5184
+ lines.push("**Body** (GraphQL)", "```graphql", req.body.graphql.query.trim(), "```", "");
5267
5185
  } else if (mode === "soap" && req.body.soap?.envelope?.trim()) {
5268
- lines.push("**Body** (SOAP)");
5269
- lines.push("```xml");
5270
- lines.push(req.body.soap.envelope.trim());
5271
- lines.push("```");
5272
- lines.push("");
5186
+ lines.push("**Body** (SOAP)", "```xml", req.body.soap.envelope.trim(), "```", "");
5273
5187
  }
5274
5188
  if (example?.sent?.body?.trim()) {
5275
5189
  const ct = example.sent.headers?.["Content-Type"] ?? example.sent.headers?.["content-type"];
5276
5190
  const lang = langForContentType(ct);
5277
- lines.push("**Example Request Body**");
5278
- lines.push("```" + lang);
5279
- lines.push(formatBody(example.sent.body, ct));
5280
- lines.push("```");
5281
- lines.push("");
5191
+ lines.push("**Example Request Body**", "```" + lang, formatBody(example.sent.body, ct), "```", "");
5282
5192
  }
5283
5193
  if (example?.response) {
5284
5194
  const ct = example.response.headers["content-type"] ?? example.response.headers["Content-Type"];
5285
5195
  const lang = langForContentType(ct);
5286
5196
  lines.push(`**Example Response** (${example.response.status})`);
5287
5197
  if (example.response.body?.trim()) {
5288
- lines.push("```" + lang);
5289
- lines.push(formatBody(example.response.body, ct));
5290
- lines.push("```");
5198
+ lines.push("```" + lang, formatBody(example.response.body, ct), "```");
5291
5199
  } else {
5292
5200
  lines.push("_(empty body)_");
5293
5201
  }
@@ -5299,11 +5207,9 @@ function folderToMarkdown(folder, requests, depth, examples) {
5299
5207
  const lines = [];
5300
5208
  const heading = "#".repeat(depth);
5301
5209
  if (folder.name !== "root") {
5302
- lines.push(`${heading} ${escMd(folder.name)}`);
5303
- lines.push("");
5210
+ lines.push(`${heading} ${escMd(folder.name)}`, "");
5304
5211
  if (folder.description?.trim()) {
5305
- lines.push(folder.description.trim());
5306
- lines.push("");
5212
+ lines.push(folder.description.trim(), "");
5307
5213
  }
5308
5214
  }
5309
5215
  for (const reqId of folder.requestIds) {
@@ -5319,15 +5225,12 @@ function folderToMarkdown(folder, requests, depth, examples) {
5319
5225
  }
5320
5226
  function generateMarkdown(payload) {
5321
5227
  const lines = [];
5322
- lines.push("# API Documentation");
5323
- lines.push("");
5228
+ lines.push("# API Documentation", "");
5324
5229
  const examples = payload.examples ?? {};
5325
5230
  for (const { collection, requests } of payload.collections) {
5326
- lines.push(`## ${escMd(collection.name)}`);
5327
- lines.push("");
5231
+ lines.push(`## ${escMd(collection.name)}`, "");
5328
5232
  if (collection.description?.trim()) {
5329
- lines.push(collection.description.trim());
5330
- lines.push("");
5233
+ lines.push(collection.description.trim(), "");
5331
5234
  }
5332
5235
  lines.push(folderToMarkdown(collection.rootFolder, requests, 3, examples));
5333
5236
  }
@@ -5470,7 +5373,7 @@ function registerDocsHandlers(ipc) {
5470
5373
  }
5471
5374
  function registerCoverageHandlers(ipc) {
5472
5375
  handle.handleIpc(ipc, handle.IPC.coverage.loadSpec, async (_e, source) => {
5473
- if (source.text && source.text.trim()) {
5376
+ if (source.text?.trim()) {
5474
5377
  const t = source.text.trim();
5475
5378
  return t.startsWith("{") ? JSON.parse(t) : jsYaml.load(t);
5476
5379
  }
@@ -5482,7 +5385,7 @@ function registerCoverageHandlers(ipc) {
5482
5385
  return ct.includes("yaml") || /\.ya?ml$/i.test(source.url) ? jsYaml.load(raw) : JSON.parse(raw);
5483
5386
  }
5484
5387
  if (source.path) {
5485
- const raw = await promises$1.readFile(source.path, "utf8");
5388
+ const raw = await promises.readFile(source.path, "utf8");
5486
5389
  return /\.ya?ml$/i.test(source.path) ? jsYaml.load(raw) : JSON.parse(raw);
5487
5390
  }
5488
5391
  throw new Error("No spec source: provide a file path, URL, or pasted text.");
@@ -5524,7 +5427,7 @@ async function resolveSnapshotSpec(relPath) {
5524
5427
  const dir = getWorkspaceDir();
5525
5428
  if (!dir) throw new Error("No workspace open - cannot resolve snapshot.");
5526
5429
  const snap = await brokerClient.loadSnapshot(dir, relPath);
5527
- const tmp = path.join(os$1.tmpdir(), `api-spector-${crypto.randomUUID()}.${snap.format === "yaml" ? "yaml" : "json"}`);
5430
+ const tmp = path.join(os.tmpdir(), `api-spector-${crypto.randomUUID()}.${snap.format === "yaml" ? "yaml" : "json"}`);
5528
5431
  await promises.writeFile(tmp, snap.spec, "utf8");
5529
5432
  return { specPath: tmp };
5530
5433
  }
@@ -5582,7 +5485,7 @@ function registerContractHandlers(ipc) {
5582
5485
  handle.handleIpc(ipc, handle.IPC.contract.exportDesignPact, async (_e, contract) => {
5583
5486
  const dir = getWorkspaceDir();
5584
5487
  if (!dir) throw new Error("No workspace open — open or save a workspace first.");
5585
- const safe = (s) => (s || "unnamed").replace(/[^a-zA-Z0-9._-]+/g, "-");
5488
+ const safe = (s) => (s || "unnamed").replaceAll(/[^a-zA-Z0-9._-]+/g, "-");
5586
5489
  const relPath = path.join("pacts", `${safe(contract.consumer)}-${safe(contract.provider)}.pact.json`);
5587
5490
  const fullPath = path.join(dir, relPath);
5588
5491
  await promises.mkdir(path.join(dir, "pacts"), { recursive: true });
@@ -5737,8 +5640,8 @@ function registerGitHandlers(ipc) {
5737
5640
  const looksLocal = !isRemote;
5738
5641
  let ahead;
5739
5642
  let behind;
5740
- const aheadM = track?.match(/ahead (\d+)/);
5741
- const behindM = track?.match(/behind (\d+)/);
5643
+ const aheadM = /ahead (\d+)/.exec(track);
5644
+ const behindM = /behind (\d+)/.exec(track);
5742
5645
  if (aheadM) ahead = Number(aheadM[1]);
5743
5646
  if (behindM) behind = Number(behindM[1]);
5744
5647
  branches.push({
@@ -5813,7 +5716,7 @@ function resolveStatus(result, filePath, staged) {
5813
5716
  if (staged) {
5814
5717
  if (result.created.includes(filePath)) return "added";
5815
5718
  if (result.deleted.includes(filePath)) return "deleted";
5816
- if (result.renamed.find((r) => r.to === filePath || r.from === filePath)) return "renamed";
5719
+ if (result.renamed.some((r) => r.to === filePath || r.from === filePath)) return "renamed";
5817
5720
  return "modified";
5818
5721
  }
5819
5722
  if (result.deleted.includes(filePath)) return "deleted";
@@ -5877,9 +5780,9 @@ async function cloudFetch(path2, method, body) {
5877
5780
  return json;
5878
5781
  }
5879
5782
  function interpolateStatic(str, vars) {
5880
- return str.replace(/\{\{([^}]+)\}\}/g, (m, key) => {
5783
+ return str.replaceAll(/\{\{([^}]+)\}\}/g, (m, key) => {
5881
5784
  const trimmed = String(key).trim();
5882
- return Object.prototype.hasOwnProperty.call(vars, trimmed) ? vars[trimmed] : m;
5785
+ return Object.hasOwn(vars, trimmed) ? vars[trimmed] : m;
5883
5786
  });
5884
5787
  }
5885
5788
  function resolveRequest(req, vars) {
@@ -5901,7 +5804,7 @@ function resolveHeaders(headers, vars) {
5901
5804
  return (headers ?? []).filter((h) => h.enabled !== false && h.key).map((h) => ({ key: interpolateStatic(h.key, vars), value: interpolateStatic(h.value ?? "", vars), enabled: true }));
5902
5805
  }
5903
5806
  function resolveBody(body, vars) {
5904
- if (!body || !body.mode || body.mode === "none") return { mode: "none" };
5807
+ if (!body?.mode || body.mode === "none") return { mode: "none" };
5905
5808
  const b = { ...body };
5906
5809
  if (body.mode === "json" && body.json != null) b.json = interpolateStatic(body.json, vars);
5907
5810
  else if (body.mode === "raw" && body.raw != null) b.raw = interpolateStatic(body.raw, vars);
@@ -5937,7 +5840,7 @@ function omitKeys(vars, names) {
5937
5840
  return out;
5938
5841
  }
5939
5842
  function slugify(name) {
5940
- return (name || "mock").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 63) || "mock";
5843
+ return (name || "mock").toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replaceAll(/^-+|-+$/g, "").slice(0, 63) || "mock";
5941
5844
  }
5942
5845
  function registerCloudHandlers(ipc) {
5943
5846
  handle.handleIpc(ipc, handle.IPC.cloud.test, async () => {
@@ -6031,7 +5934,7 @@ function registerCloudHandlers(ipc) {
6031
5934
  }
6032
5935
  const REGISTRY_URL = "https://registry.npmjs.org/@testsmith/api-spector/latest";
6033
5936
  function isNewer(a, b) {
6034
- const core = (v) => v.split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
5937
+ const core = (v) => v.split("-")[0].split(".").map((n) => Number.parseInt(n, 10) || 0);
6035
5938
  const [a0, a1, a2] = core(a);
6036
5939
  const [b0, b1, b2] = core(b);
6037
5940
  if (a0 !== b0) return a0 > b0;
@@ -6039,7 +5942,7 @@ function isNewer(a, b) {
6039
5942
  return a2 > b2;
6040
5943
  }
6041
5944
  async function checkForUpdate() {
6042
- const current = "0.6.2";
5945
+ const current = "0.6.4";
6043
5946
  try {
6044
5947
  const res = await fetch(REGISTRY_URL, { signal: AbortSignal.timeout(4e3) });
6045
5948
  if (!res.ok) return null;
@@ -6076,7 +5979,7 @@ function parseOpenFromGit(deepLink) {
6076
5979
  const action = u.hostname || u.pathname.replace(/^\/+/, "").split("/")[0];
6077
5980
  if (action !== "open-from-git") return null;
6078
5981
  const repo = u.searchParams.get("url");
6079
- return repo && repo.trim() ? repo.trim() : null;
5982
+ return repo?.trim() ? repo.trim() : null;
6080
5983
  }
6081
5984
  function handleDeepLink(deepLink) {
6082
5985
  const repo = parseOpenFromGit(deepLink);
@@ -6125,15 +6028,15 @@ function loadAppIcon() {
6125
6028
  path.join(__dirname, "../../build/icon.png")
6126
6029
  ];
6127
6030
  for (const p of pngCandidates) {
6128
- if (fs$1.existsSync(p)) return electron.nativeImage.createFromPath(p);
6031
+ if (fs.existsSync(p)) return electron.nativeImage.createFromPath(p);
6129
6032
  }
6130
6033
  const svgCandidates = [
6131
6034
  path.join(electron.app.getAppPath(), "resources/icon.svg"),
6132
6035
  path.join(__dirname, "../../resources/icon.svg")
6133
6036
  ];
6134
6037
  for (const p of svgCandidates) {
6135
- if (fs$1.existsSync(p)) {
6136
- const dataUrl = "data:image/svg+xml;base64," + fs$1.readFileSync(p).toString("base64");
6038
+ if (fs.existsSync(p)) {
6039
+ const dataUrl = "data:image/svg+xml;base64," + fs.readFileSync(p).toString("base64");
6137
6040
  return electron.nativeImage.createFromDataURL(dataUrl);
6138
6041
  }
6139
6042
  }