@testsmith/api-spector 0.7.6 → 0.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/bin/cli.js +4 -0
  2. package/out/main/chunks/{broker-client-5IyCTx40.js → broker-client-BYpWeOWT.js} +4 -1
  3. package/out/main/chunks/{flow-engine-BVzjz4lJ.js → flow-engine-BxwjEy37.js} +66 -1
  4. package/out/main/chunks/{handle-DRmKOL-s.js → handle-Crv1i9hb.js} +61 -0
  5. package/out/main/chunks/{import-B5T5wDu7.js → import-DTcZMRNu.js} +2 -2
  6. package/out/main/chunks/perf-report-ChaO3K0w.js +124 -0
  7. package/out/main/chunks/perf-run-uZBqnG3S.js +578 -0
  8. package/out/main/chunks/{request-collection-KGPuvN7o.js → request-collection-gkOYlWml.js} +68 -11
  9. package/out/main/chunks/{request-exec-CwJ8_OKC.js → request-exec-Cv2jkrO1.js} +458 -12
  10. package/out/main/chunks/run-concurrent-468tH0AX.js +26 -0
  11. package/out/main/chunks/{soap-handler-DTfFnzNY.js → soap-handler-Brr7BPWO.js} +2 -2
  12. package/out/main/contract.js +6 -3
  13. package/out/main/flow.js +8 -5
  14. package/out/main/index.js +1338 -179
  15. package/out/main/lib.js +6 -3
  16. package/out/main/perf-worker.js +45 -0
  17. package/out/main/perf.js +253 -0
  18. package/out/main/runner.js +181 -7
  19. package/out/main/socketio.js +172 -0
  20. package/out/main/wsdl.js +3 -3
  21. package/out/preload/index.js +127 -0
  22. package/out/renderer/assets/{GraphQLEditor-CDLI1OHR.js → GraphQLEditor-Dv2K4wdd.js} +59 -14
  23. package/out/renderer/assets/{SoapEditor-CSUFJk7c.js → SoapEditor-Cnz3M4Dz.js} +3 -3
  24. package/out/renderer/assets/ajv-CJPwFjiP.js +6718 -0
  25. package/out/renderer/assets/{codemirror-F6m3Hm1M.js → codemirror-CdIFBPs2.js} +1214 -1207
  26. package/out/renderer/assets/{index-8bbf24uI.js → index-CtCdFiRx.js} +16957 -13834
  27. package/out/renderer/assets/index-DkhdKzQJ.css +627 -0
  28. package/out/renderer/index.html +4 -4
  29. package/package.json +12 -6
  30. package/out/renderer/assets/ajv-2MAg6kGF.js +0 -6153
  31. package/out/renderer/assets/index-CYArpqm5.css +0 -627
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  const undici = require("undici");
25
25
  const promises$1 = require("fs/promises");
26
26
  const nodeTls = require("tls");
27
- const handle = require("./handle-DRmKOL-s.js");
27
+ const handle = require("./handle-Crv1i9hb.js");
28
28
  const fs = require("node:fs");
29
29
  const os = require("node:os");
30
30
  const path = require("node:path");
@@ -35,6 +35,9 @@ const vm = require("node:vm");
35
35
  const tv4 = require("tv4");
36
36
  const jsonpathPlus = require("jsonpath-plus");
37
37
  const xmldom = require("@xmldom/xmldom");
38
+ const mqtt = require("mqtt");
39
+ const WebSocket = require("ws");
40
+ const socket_ioClient = require("socket.io-client");
38
41
  const http = require("node:http");
39
42
  const Ajv = require("ajv");
40
43
  function _interopNamespaceDefault(e) {
@@ -719,6 +722,11 @@ function registerSecretHandlers(ipc) {
719
722
  handle.handleIpc(ipc, handle.IPC.secret.setMasterKey, (_e, value) => {
720
723
  process.env[MASTER_KEY_ENV] = value;
721
724
  });
725
+ handle.handleIpc(ipc, handle.IPC.secret.encryptWithMasterKey, (_e, plaintext) => {
726
+ const password = process.env[MASTER_KEY_ENV];
727
+ if (!password) throw new Error("Master password is not set in this process.");
728
+ return encryptSecret(plaintext, password);
729
+ });
722
730
  handle.handleIpc(ipc, handle.IPC.secret.set, async (_e, ref, value) => {
723
731
  const ss = getSafeStorage();
724
732
  if (!ss?.isEncryptionAvailable()) {
@@ -737,6 +745,20 @@ function registerSecretHandlers(ipc) {
737
745
  }
738
746
  });
739
747
  }
748
+ function encryptSecret(plaintext, password) {
749
+ const salt = crypto.randomBytes(16);
750
+ const iv = crypto.randomBytes(12);
751
+ const key = crypto.pbkdf2Sync(password, salt, 1e5, 32, "sha256");
752
+ const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
753
+ const ciphertext = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
754
+ const authTag = cipher.getAuthTag();
755
+ return {
756
+ secretEncrypted: Buffer.concat([ciphertext, authTag]).toString("base64"),
757
+ secretSalt: salt.toString("base64"),
758
+ secretIv: iv.toString("base64"),
759
+ secretHash: crypto.createHash("sha256").update(plaintext, "utf8").digest("hex").slice(0, 8)
760
+ };
761
+ }
740
762
  function decryptSecret(encrypted, salt, iv, password) {
741
763
  const saltBuf = Buffer.from(salt, "base64");
742
764
  const ivBuf = Buffer.from(iv, "base64");
@@ -886,9 +908,9 @@ async function buildEnvVars(environment) {
886
908
  }
887
909
  return vars;
888
910
  }
889
- function mergeVars(envVars, collectionVars, globals2, localVars = {}, dynamicVars = {}) {
911
+ function mergeVars(envVars, collectionVars, globals2, localVars = {}, dynamicVars = {}, folderVars = {}) {
890
912
  const out = {};
891
- for (const src of [dynamicVars, globals2, collectionVars, envVars, localVars]) {
913
+ for (const src of [dynamicVars, globals2, collectionVars, folderVars, envVars, localVars]) {
892
914
  for (const [k, v] of Object.entries(src ?? {})) {
893
915
  if (v == null) continue;
894
916
  out[k] = v;
@@ -1072,6 +1094,7 @@ class AssertionError extends Error {
1072
1094
  }
1073
1095
  function buildAt(ctx, testResults, consoleOutput) {
1074
1096
  const { envVars, collectionVars, globals: globals2, localVars, piiMaskPatterns = [] } = ctx;
1097
+ const folderVars = ctx.folderVars ?? {};
1075
1098
  const isSensitiveKey = (key) => piiMaskPatterns.some((p) => key.toLowerCase().includes(p.toLowerCase()));
1076
1099
  function makeVarScope(store, scopeName) {
1077
1100
  return {
@@ -1094,9 +1117,10 @@ function buildAt(ctx, testResults, consoleOutput) {
1094
1117
  variables: makeVarScope(localVars, "variables"),
1095
1118
  environment: makeVarScope(envVars, "environment"),
1096
1119
  collectionVariables: makeVarScope(collectionVars, "collectionVariables"),
1120
+ folder: makeVarScope(folderVars, "folder"),
1097
1121
  globals: makeVarScope(globals2, "globals"),
1098
1122
  // Convenience: get/set across all scopes (local wins)
1099
- variables_get: (key) => localVars[key] ?? envVars[key] ?? collectionVars[key] ?? globals2[key] ?? null,
1123
+ variables_get: (key) => localVars[key] ?? envVars[key] ?? folderVars[key] ?? collectionVars[key] ?? globals2[key] ?? null,
1100
1124
  variables_set: (key, value) => {
1101
1125
  localVars[key] = String(value);
1102
1126
  },
@@ -1155,6 +1179,14 @@ function buildAt(ctx, testResults, consoleOutput) {
1155
1179
  return String(binCode % 10 ** digits).padStart(digits, "0");
1156
1180
  }
1157
1181
  };
1182
+ const collected = ctx.messages ?? ctx.mqttMessages;
1183
+ if (collected) {
1184
+ sp.messages = collected.map((m) => ({
1185
+ ...m,
1186
+ json: () => JSON.parse(m.payload),
1187
+ text: () => m.payload
1188
+ }));
1189
+ }
1158
1190
  if (ctx.response) {
1159
1191
  const resp = ctx.response;
1160
1192
  let parsedJson = void 0;
@@ -1230,12 +1262,17 @@ async function runScript(code, ctx, timeoutMs = 5e3) {
1230
1262
  const collectionCopy = { ...ctx.collectionVars };
1231
1263
  const globalsCopy = { ...ctx.globals };
1232
1264
  const localVarsCopy = { ...ctx.localVars };
1265
+ const folderCopy = { ...ctx.folderVars ?? {} };
1233
1266
  const scriptCtx = {
1234
1267
  envVars: envVarsCopy,
1235
1268
  collectionVars: collectionCopy,
1236
1269
  globals: globalsCopy,
1237
1270
  localVars: localVarsCopy,
1271
+ folderVars: folderCopy,
1238
1272
  response: ctx.response,
1273
+ data: ctx.data,
1274
+ mqttMessages: ctx.mqttMessages,
1275
+ messages: ctx.messages,
1239
1276
  piiMaskPatterns: ctx.piiMaskPatterns
1240
1277
  };
1241
1278
  const sandbox = buildSandbox(scriptCtx, testResults, consoleOutput, faker);
@@ -1250,6 +1287,7 @@ async function runScript(code, ctx, timeoutMs = 5e3) {
1250
1287
  updatedCollectionVars: collectionCopy,
1251
1288
  updatedGlobals: globalsCopy,
1252
1289
  updatedLocalVars: localVarsCopy,
1290
+ updatedFolderVars: folderCopy,
1253
1291
  error: message
1254
1292
  };
1255
1293
  }
@@ -1259,7 +1297,8 @@ async function runScript(code, ctx, timeoutMs = 5e3) {
1259
1297
  updatedEnvVars: envVarsCopy,
1260
1298
  updatedCollectionVars: collectionCopy,
1261
1299
  updatedGlobals: globalsCopy,
1262
- updatedLocalVars: localVarsCopy
1300
+ updatedLocalVars: localVarsCopy,
1301
+ updatedFolderVars: folderCopy
1263
1302
  };
1264
1303
  }
1265
1304
  async function evaluateExpression(code, ctx, timeoutMs = 5e3) {
@@ -1338,6 +1377,7 @@ class StreamFramer {
1338
1377
  constructor(mode) {
1339
1378
  this.mode = mode;
1340
1379
  }
1380
+ mode;
1341
1381
  carry = "";
1342
1382
  /** Feed a decoded text chunk; returns the frames that completed. */
1343
1383
  push(text) {
@@ -1500,6 +1540,314 @@ async function readStream(body, kind, startMs, opts = {}) {
1500
1540
  }
1501
1541
  return { events, text, close, firstEventMs: Math.max(firstEventMs, 0) };
1502
1542
  }
1543
+ async function runMqttTest(cfg) {
1544
+ const messages = [];
1545
+ const collectMs = Math.max(0, Math.min(6e4, Math.floor(cfg.collectMs ?? 2e3)));
1546
+ const clientId = cfg.clientId?.trim() || `api-spector-test-${Date.now().toString(36)}-${Math.random().toString(16).slice(2, 10)}`;
1547
+ return new Promise((resolve2) => {
1548
+ let settled = false;
1549
+ let connected = false;
1550
+ let lastError = "";
1551
+ let client;
1552
+ let timer;
1553
+ const runAssertions = async (connectError) => {
1554
+ if (settled) return;
1555
+ settled = true;
1556
+ if (timer) clearTimeout(timer);
1557
+ try {
1558
+ client?.end(true);
1559
+ } catch {
1560
+ }
1561
+ if (connectError && messages.length === 0) {
1562
+ resolve2({ testResults: [], consoleOutput: [], error: connectError, messages, clientId });
1563
+ return;
1564
+ }
1565
+ const out = await runScript(cfg.script ?? "", {
1566
+ envVars: cfg.envVars ?? {},
1567
+ collectionVars: cfg.collectionVars ?? {},
1568
+ globals: cfg.globals ?? {},
1569
+ localVars: cfg.localVars ?? {},
1570
+ mqttMessages: messages
1571
+ });
1572
+ resolve2({ testResults: out.testResults, consoleOutput: out.consoleOutput, error: out.error, messages, clientId });
1573
+ };
1574
+ try {
1575
+ client = mqtt.connect(cfg.url, {
1576
+ clientId,
1577
+ username: cfg.username?.trim() || void 0,
1578
+ password: cfg.password ? cfg.password : void 0,
1579
+ clean: cfg.cleanSession !== false,
1580
+ reconnectPeriod: 0,
1581
+ connectTimeout: 15e3
1582
+ });
1583
+ } catch (err) {
1584
+ resolve2({ testResults: [], consoleOutput: [], error: err instanceof Error ? err.message : String(err), messages, clientId });
1585
+ return;
1586
+ }
1587
+ client.on("connect", () => {
1588
+ connected = true;
1589
+ const publish = () => {
1590
+ const topic = cfg.publishTopic?.trim();
1591
+ if (!topic) return;
1592
+ if (/[+#]/.test(topic)) return;
1593
+ client.publish(topic, cfg.publishPayload ?? "", { qos: cfg.qos ?? 0, retain: Boolean(cfg.retain) });
1594
+ };
1595
+ const sub = cfg.subscribeTopic?.trim();
1596
+ if (sub) {
1597
+ client.subscribe(sub, { qos: cfg.qos ?? 0 }, () => setTimeout(publish, 100));
1598
+ } else {
1599
+ publish();
1600
+ }
1601
+ timer = setTimeout(() => void runAssertions(), collectMs);
1602
+ });
1603
+ client.on("message", (topic, payload, packet) => {
1604
+ messages.push({ topic, payload: payload.toString("utf8"), qos: packet?.qos, retain: Boolean(packet?.retain), timestamp: Date.now() });
1605
+ });
1606
+ client.on("error", (err) => {
1607
+ const code = err?.code;
1608
+ lastError = [err?.message, code].filter(Boolean).join(" ");
1609
+ void runAssertions(err?.message || "Could not connect to the broker");
1610
+ });
1611
+ client.on("close", () => {
1612
+ if (settled) return;
1613
+ void runAssertions(connected ? `The test connection was dropped by the broker before the window finished${lastError ? ` [${lastError}]` : ""}. Common causes: publishing to a topic that contains a wildcard (+ or #), or another connection using the same client id. Check the broker's log.` : `Could not connect to the broker${lastError ? ` [${lastError}]` : ""}`);
1614
+ });
1615
+ });
1616
+ }
1617
+ function clampWindow(ms) {
1618
+ return Math.max(0, Math.min(6e4, Math.floor(ms ?? 3e3)));
1619
+ }
1620
+ async function assertOver(cfg, messages) {
1621
+ const out = await runScript(cfg.script ?? "", {
1622
+ envVars: cfg.envVars ?? {},
1623
+ collectionVars: cfg.collectionVars ?? {},
1624
+ globals: cfg.globals ?? {},
1625
+ localVars: cfg.localVars ?? {},
1626
+ messages
1627
+ });
1628
+ return { testResults: out.testResults, consoleOutput: out.consoleOutput, error: out.error, messages };
1629
+ }
1630
+ function errorResult(error, messages) {
1631
+ return { testResults: [], consoleOutput: [], error, messages };
1632
+ }
1633
+ function parseJsonObject(text, label) {
1634
+ if (!text || !text.trim()) return void 0;
1635
+ let parsed;
1636
+ try {
1637
+ parsed = JSON.parse(text);
1638
+ } catch {
1639
+ throw new Error(`${label} is not valid JSON`);
1640
+ }
1641
+ return parsed && typeof parsed === "object" ? parsed : void 0;
1642
+ }
1643
+ async function runWsTest(cfg) {
1644
+ const messages = [];
1645
+ const collectMs = clampWindow(cfg.collectMs);
1646
+ return new Promise((resolve2) => {
1647
+ let settled = false;
1648
+ let connected = false;
1649
+ let lastError = "";
1650
+ let ws;
1651
+ let timer;
1652
+ const finish = async (connectError) => {
1653
+ if (settled) return;
1654
+ settled = true;
1655
+ if (timer) clearTimeout(timer);
1656
+ try {
1657
+ ws?.close();
1658
+ } catch {
1659
+ }
1660
+ if (connectError && messages.length === 0) {
1661
+ resolve2(errorResult(connectError, messages));
1662
+ return;
1663
+ }
1664
+ resolve2(await assertOver(cfg, messages));
1665
+ };
1666
+ try {
1667
+ ws = new WebSocket(cfg.url, { headers: cfg.headers });
1668
+ } catch (err) {
1669
+ resolve2(errorResult(err instanceof Error ? err.message : String(err), messages));
1670
+ return;
1671
+ }
1672
+ ws.on("open", () => {
1673
+ connected = true;
1674
+ if (cfg.sendMessage) {
1675
+ try {
1676
+ ws.send(cfg.sendMessage);
1677
+ } catch {
1678
+ }
1679
+ }
1680
+ timer = setTimeout(() => void finish(), collectMs);
1681
+ });
1682
+ ws.on("message", (data) => {
1683
+ const text = data.toString();
1684
+ messages.push({ payload: text, data: text, timestamp: Date.now() });
1685
+ });
1686
+ ws.on("error", (err) => {
1687
+ lastError = err.message;
1688
+ void finish(err.message || "Could not connect to the server");
1689
+ });
1690
+ ws.on("close", () => {
1691
+ if (settled) return;
1692
+ void finish(connected ? `The WebSocket was closed before the window finished${lastError ? ` [${lastError}]` : ""}.` : `Could not connect to the server${lastError ? ` [${lastError}]` : ""}`);
1693
+ });
1694
+ });
1695
+ }
1696
+ function targetUrl(url, namespace) {
1697
+ const ns = (namespace ?? "").trim();
1698
+ if (!ns || ns === "/") return url;
1699
+ return url.replace(/\/+$/, "") + (ns.startsWith("/") ? ns : `/${ns}`);
1700
+ }
1701
+ async function runSocketIoTest(cfg) {
1702
+ const messages = [];
1703
+ const collectMs = clampWindow(cfg.collectMs);
1704
+ let auth;
1705
+ try {
1706
+ auth = parseJsonObject(cfg.auth, "Auth");
1707
+ } catch (e) {
1708
+ return errorResult(e instanceof Error ? e.message : String(e), messages);
1709
+ }
1710
+ let emitArgs = [];
1711
+ if (cfg.emitArgs && cfg.emitArgs.trim()) {
1712
+ try {
1713
+ const parsed = JSON.parse(cfg.emitArgs);
1714
+ emitArgs = Array.isArray(parsed) ? parsed : [parsed];
1715
+ } catch {
1716
+ return errorResult("Emit arguments are not valid JSON", messages);
1717
+ }
1718
+ }
1719
+ return new Promise((resolve2) => {
1720
+ let settled = false;
1721
+ let socket;
1722
+ let timer;
1723
+ const finish = async (connectError) => {
1724
+ if (settled) return;
1725
+ settled = true;
1726
+ if (timer) clearTimeout(timer);
1727
+ try {
1728
+ socket?.disconnect();
1729
+ } catch {
1730
+ }
1731
+ if (connectError && messages.length === 0) {
1732
+ resolve2(errorResult(connectError, messages));
1733
+ return;
1734
+ }
1735
+ resolve2(await assertOver(cfg, messages));
1736
+ };
1737
+ try {
1738
+ socket = socket_ioClient.io(targetUrl(cfg.url, cfg.namespace), {
1739
+ path: cfg.path?.trim() || "/socket.io",
1740
+ transports: ["websocket", "polling"],
1741
+ extraHeaders: cfg.headers,
1742
+ auth,
1743
+ forceNew: true,
1744
+ reconnection: false
1745
+ });
1746
+ } catch (err) {
1747
+ resolve2(errorResult(err instanceof Error ? err.message : String(err), messages));
1748
+ return;
1749
+ }
1750
+ socket.on("connect", () => {
1751
+ const ev = cfg.emitEvent?.trim();
1752
+ if (ev) setTimeout(() => {
1753
+ try {
1754
+ socket.emit(ev, ...emitArgs);
1755
+ } catch {
1756
+ }
1757
+ }, 100);
1758
+ timer = setTimeout(() => void finish(), collectMs);
1759
+ });
1760
+ socket.on("connect_error", (err) => {
1761
+ void finish(`Could not connect to the server${err.message ? ` [${err.message}]` : ""}`);
1762
+ });
1763
+ socket.onAny((event, ...args) => {
1764
+ const payload = JSON.stringify(args.length <= 1 ? args[0] ?? null : args);
1765
+ messages.push({ payload, event, args, timestamp: Date.now() });
1766
+ });
1767
+ });
1768
+ }
1769
+ const GQL_SUB_ID = "1";
1770
+ async function runGraphqlWsTest(cfg) {
1771
+ const messages = [];
1772
+ const collectMs = clampWindow(cfg.collectMs);
1773
+ let variables;
1774
+ let connectionParams;
1775
+ try {
1776
+ variables = parseJsonObject(cfg.variables, "Variables");
1777
+ connectionParams = parseJsonObject(cfg.connectionParams, "Connection params");
1778
+ } catch (e) {
1779
+ return errorResult(e instanceof Error ? e.message : String(e), messages);
1780
+ }
1781
+ return new Promise((resolve2) => {
1782
+ let settled = false;
1783
+ let connected = false;
1784
+ let lastError = "";
1785
+ let ws;
1786
+ let timer;
1787
+ const finish = async (connectError) => {
1788
+ if (settled) return;
1789
+ settled = true;
1790
+ if (timer) clearTimeout(timer);
1791
+ try {
1792
+ ws?.close();
1793
+ } catch {
1794
+ }
1795
+ if (connectError && messages.length === 0) {
1796
+ resolve2(errorResult(connectError, messages));
1797
+ return;
1798
+ }
1799
+ resolve2(await assertOver(cfg, messages));
1800
+ };
1801
+ try {
1802
+ ws = new WebSocket(cfg.url, "graphql-transport-ws", { headers: cfg.headers });
1803
+ } catch (err) {
1804
+ resolve2(errorResult(err instanceof Error ? err.message : String(err), messages));
1805
+ return;
1806
+ }
1807
+ ws.on("open", () => {
1808
+ connected = true;
1809
+ ws.send(JSON.stringify({ type: "connection_init", payload: connectionParams }));
1810
+ timer = setTimeout(() => void finish(), collectMs);
1811
+ });
1812
+ ws.on("message", (raw) => {
1813
+ let msg;
1814
+ try {
1815
+ msg = JSON.parse(raw.toString());
1816
+ } catch {
1817
+ return;
1818
+ }
1819
+ switch (msg.type) {
1820
+ case "connection_ack":
1821
+ ws.send(JSON.stringify({ id: GQL_SUB_ID, type: "subscribe", payload: { query: cfg.query, variables, operationName: cfg.operationName } }));
1822
+ break;
1823
+ case "next": {
1824
+ const payload = msg.payload;
1825
+ const data = payload?.data;
1826
+ messages.push({ payload: JSON.stringify(data ?? payload ?? null), data, errors: payload?.errors, timestamp: Date.now() });
1827
+ break;
1828
+ }
1829
+ case "error":
1830
+ lastError = JSON.stringify(msg.payload);
1831
+ void finish(`The subscription returned an error${lastError ? ` [${lastError}]` : ""}`);
1832
+ break;
1833
+ case "complete":
1834
+ void finish();
1835
+ break;
1836
+ case "ping":
1837
+ ws.send(JSON.stringify({ type: "pong" }));
1838
+ break;
1839
+ }
1840
+ });
1841
+ ws.on("error", (err) => {
1842
+ lastError = err.message;
1843
+ void finish(err.message || "Could not connect to the server");
1844
+ });
1845
+ ws.on("close", () => {
1846
+ if (settled) return;
1847
+ void finish(connected ? `The subscription connection was closed before the window finished${lastError ? ` [${lastError}]` : ""}.` : `Could not connect to the server${lastError ? ` [${lastError}]` : ""}`);
1848
+ });
1849
+ });
1850
+ }
1503
1851
  const SIGNATURE = Buffer.from("NTLMSSP\0", "latin1");
1504
1852
  const F_UNICODE = 1;
1505
1853
  const F_OEM = 2;
@@ -1676,7 +2024,7 @@ async function buildAuthHeaders(auth, vars) {
1676
2024
  const username = interpolate(auth.username ?? "", vars);
1677
2025
  headers["Authorization"] = `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
1678
2026
  }
1679
- if (auth.type === "apikey" && auth.apiKeyIn === "header") {
2027
+ if (auth.type === "apikey" && (auth.apiKeyIn ?? "header") === "header") {
1680
2028
  let value = auth.apiKeyValue ?? "";
1681
2029
  if (!value && auth.apiKeySecretRef) value = await getSecret(auth.apiKeySecretRef) ?? "";
1682
2030
  value = interpolate(value, vars);
@@ -2267,6 +2615,7 @@ async function resolveInlineSecrets(req) {
2267
2615
  async function executeRunnerRequest(opts) {
2268
2616
  const { req, collectionVars, envVars, globals: globals2, dispatcher, piiMaskPatterns, proxy, tls, onScriptOutput } = opts;
2269
2617
  let { localVars } = opts;
2618
+ const folderVars = opts.folderVars ?? {};
2270
2619
  applyRequestDefaults(req);
2271
2620
  const base = {
2272
2621
  requestId: req.id,
@@ -2277,13 +2626,99 @@ async function executeRunnerRequest(opts) {
2277
2626
  };
2278
2627
  const dynamicVars = await buildDynamicVars();
2279
2628
  if (localVars && Object.keys(localVars).length > 0) {
2280
- localVars = resolveDataRow(localVars, mergeVars(envVars, collectionVars, globals2, {}, dynamicVars));
2629
+ localVars = resolveDataRow(localVars, mergeVars(envVars, collectionVars, globals2, {}, dynamicVars, folderVars));
2281
2630
  }
2282
- let vars = mergeVars(envVars, collectionVars, globals2, localVars, dynamicVars);
2631
+ let vars = mergeVars(envVars, collectionVars, globals2, localVars, dynamicVars, folderVars);
2283
2632
  let updatedEnvVars = { ...envVars };
2284
2633
  let updatedCollectionVars = { ...collectionVars };
2285
2634
  let updatedGlobals = { ...globals2 };
2635
+ let updatedFolderVars = { ...folderVars };
2286
2636
  let preScriptError;
2637
+ if (req.protocol === "mqtt") {
2638
+ const m = req.mqtt ?? {};
2639
+ const istr = (s) => s ? interpolate(s, vars) : void 0;
2640
+ const mqttStart = Date.now();
2641
+ const testRes = await runMqttTest({
2642
+ url: interpolate(req.url, vars),
2643
+ clientId: istr(m.clientId),
2644
+ username: istr(m.username),
2645
+ password: m.password ? interpolate(m.password, vars) : void 0,
2646
+ cleanSession: m.cleanSession !== false,
2647
+ subscribeTopic: istr(m.subscribeTopic),
2648
+ qos: m.qos,
2649
+ publishTopic: istr(m.publishTopic),
2650
+ publishPayload: istr(m.publishPayload),
2651
+ retain: m.retain,
2652
+ collectMs: m.collectMs ?? 2e3,
2653
+ script: m.testScript ?? "",
2654
+ envVars,
2655
+ collectionVars,
2656
+ globals: globals2,
2657
+ localVars
2658
+ });
2659
+ const status = testRes.error ? "error" : testRes.testResults.length === 0 ? "skipped" : testRes.testResults.every((t) => t.passed) ? "passed" : "failed";
2660
+ return {
2661
+ result: {
2662
+ ...base,
2663
+ status,
2664
+ durationMs: Date.now() - mqttStart,
2665
+ testResults: testRes.testResults,
2666
+ consoleOutput: testRes.consoleOutput,
2667
+ error: testRes.error
2668
+ },
2669
+ updatedEnvVars,
2670
+ updatedCollectionVars,
2671
+ updatedGlobals,
2672
+ updatedLocalVars: localVars,
2673
+ updatedFolderVars
2674
+ };
2675
+ }
2676
+ if (req.protocol === "websocket" || req.protocol === "socketio") {
2677
+ const rtHeaders = {};
2678
+ for (const h of req.headers ?? []) if (h.enabled && h.key) rtHeaders[interpolate(h.key, vars)] = interpolate(h.value, vars);
2679
+ const istr = (s) => s ? interpolate(s, vars) : void 0;
2680
+ const rtStart = Date.now();
2681
+ const testRes = req.protocol === "websocket" ? await runWsTest({
2682
+ url: interpolate(req.url, vars),
2683
+ headers: rtHeaders,
2684
+ sendMessage: istr(req.websocket?.sendMessage),
2685
+ collectMs: req.websocket?.collectMs ?? 3e3,
2686
+ script: req.websocket?.testScript ?? "",
2687
+ envVars,
2688
+ collectionVars,
2689
+ globals: globals2,
2690
+ localVars
2691
+ }) : await runSocketIoTest({
2692
+ url: interpolate(req.url, vars),
2693
+ namespace: req.socketio?.namespace,
2694
+ auth: istr(req.socketio?.auth),
2695
+ headers: rtHeaders,
2696
+ emitEvent: istr(req.socketio?.emitEvent),
2697
+ emitArgs: istr(req.socketio?.emitArgs),
2698
+ collectMs: req.socketio?.collectMs ?? 3e3,
2699
+ script: req.socketio?.testScript ?? "",
2700
+ envVars,
2701
+ collectionVars,
2702
+ globals: globals2,
2703
+ localVars
2704
+ });
2705
+ const status = testRes.error ? "error" : testRes.testResults.length === 0 ? "skipped" : testRes.testResults.every((t) => t.passed) ? "passed" : "failed";
2706
+ return {
2707
+ result: {
2708
+ ...base,
2709
+ status,
2710
+ durationMs: Date.now() - rtStart,
2711
+ testResults: testRes.testResults,
2712
+ consoleOutput: testRes.consoleOutput,
2713
+ error: testRes.error
2714
+ },
2715
+ updatedEnvVars,
2716
+ updatedCollectionVars,
2717
+ updatedGlobals,
2718
+ updatedLocalVars: localVars,
2719
+ updatedFolderVars
2720
+ };
2721
+ }
2287
2722
  let inlineSecrets = {};
2288
2723
  try {
2289
2724
  inlineSecrets = await resolveInlineSecrets(req);
@@ -2298,7 +2733,8 @@ async function executeRunnerRequest(opts) {
2298
2733
  updatedEnvVars,
2299
2734
  updatedCollectionVars,
2300
2735
  updatedGlobals,
2301
- updatedLocalVars: localVars
2736
+ updatedLocalVars: localVars,
2737
+ updatedFolderVars
2302
2738
  };
2303
2739
  }
2304
2740
  if (Object.keys(inlineSecrets).length) vars = { ...vars, ...inlineSecrets };
@@ -2308,6 +2744,7 @@ async function executeRunnerRequest(opts) {
2308
2744
  collectionVars: { ...collectionVars },
2309
2745
  globals: { ...globals2 },
2310
2746
  localVars: { ...localVars },
2747
+ folderVars: { ...updatedFolderVars },
2311
2748
  piiMaskPatterns
2312
2749
  });
2313
2750
  preScriptError = r.error;
@@ -2315,10 +2752,11 @@ async function executeRunnerRequest(opts) {
2315
2752
  updatedEnvVars = r.updatedEnvVars;
2316
2753
  updatedCollectionVars = r.updatedCollectionVars;
2317
2754
  updatedGlobals = r.updatedGlobals;
2755
+ updatedFolderVars = r.updatedFolderVars;
2318
2756
  patchGlobals(r.updatedGlobals);
2319
2757
  await persistGlobals();
2320
2758
  onScriptOutput?.("pre", r.consoleOutput, r.error);
2321
- vars = mergeVars(updatedEnvVars, updatedCollectionVars, updatedGlobals, localVars, dynamicVars);
2759
+ vars = mergeVars(updatedEnvVars, updatedCollectionVars, updatedGlobals, localVars, dynamicVars, updatedFolderVars);
2322
2760
  if (Object.keys(inlineSecrets).length) vars = { ...vars, ...inlineSecrets };
2323
2761
  }
2324
2762
  const resolvedUrl = buildUrl(req.url, req.params, vars);
@@ -2347,6 +2785,7 @@ async function executeRunnerRequest(opts) {
2347
2785
  collectionVars: updatedCollectionVars,
2348
2786
  globals: updatedGlobals,
2349
2787
  localVars,
2788
+ folderVars: updatedFolderVars,
2350
2789
  response: scriptResponse,
2351
2790
  piiMaskPatterns
2352
2791
  });
@@ -2356,6 +2795,7 @@ async function executeRunnerRequest(opts) {
2356
2795
  updatedEnvVars = r.updatedEnvVars;
2357
2796
  updatedCollectionVars = r.updatedCollectionVars;
2358
2797
  updatedGlobals = r.updatedGlobals;
2798
+ updatedFolderVars = r.updatedFolderVars;
2359
2799
  localVars = r.updatedLocalVars;
2360
2800
  patchGlobals(r.updatedGlobals);
2361
2801
  await persistGlobals();
@@ -2395,7 +2835,8 @@ async function executeRunnerRequest(opts) {
2395
2835
  updatedEnvVars,
2396
2836
  updatedCollectionVars,
2397
2837
  updatedGlobals,
2398
- updatedLocalVars: localVars
2838
+ updatedLocalVars: localVars,
2839
+ updatedFolderVars
2399
2840
  };
2400
2841
  } catch (err) {
2401
2842
  return {
@@ -2409,7 +2850,8 @@ async function executeRunnerRequest(opts) {
2409
2850
  updatedEnvVars,
2410
2851
  updatedCollectionVars,
2411
2852
  updatedGlobals,
2412
- updatedLocalVars: localVars
2853
+ updatedLocalVars: localVars,
2854
+ updatedFolderVars
2413
2855
  };
2414
2856
  }
2415
2857
  }
@@ -2491,7 +2933,11 @@ exports.registerSecretProvider = registerSecretProvider;
2491
2933
  exports.registeredSchemes = registeredSchemes;
2492
2934
  exports.resolveDataRow = resolveDataRow;
2493
2935
  exports.resolveExternalSecret = resolveExternalSecret;
2936
+ exports.runGraphqlWsTest = runGraphqlWsTest;
2937
+ exports.runMqttTest = runMqttTest;
2494
2938
  exports.runScript = runScript;
2939
+ exports.runSocketIoTest = runSocketIoTest;
2940
+ exports.runWsTest = runWsTest;
2495
2941
  exports.setGlobals = setGlobals;
2496
2942
  exports.setSecretsConfig = setSecretsConfig;
2497
2943
  exports.syntheticHttpFailure = syntheticHttpFailure;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ async function runPlanConcurrent(concurrency, items, isBarrier, run) {
3
+ const n = Math.max(1, Math.floor(concurrency));
4
+ const pool = [];
5
+ const settleOne = async () => {
6
+ const done = await Promise.race(pool.map((x) => x.p));
7
+ const pi = pool.findIndex((x) => x.i === done);
8
+ if (pi >= 0) pool.splice(pi, 1);
9
+ };
10
+ const drain = async () => {
11
+ while (pool.length) await settleOne();
12
+ };
13
+ for (let i = 0; i < items.length; i++) {
14
+ const item = items[i];
15
+ if (isBarrier(item)) {
16
+ await drain();
17
+ await run(item, i);
18
+ } else {
19
+ const idx = i;
20
+ pool.push({ p: run(item, idx).then(() => idx), i: idx });
21
+ if (pool.length >= n) await settleOne();
22
+ }
23
+ }
24
+ await drain();
25
+ }
26
+ exports.runPlanConcurrent = runPlanConcurrent;
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- const handle = require("./handle-DRmKOL-s.js");
2
+ const handle = require("./handle-Crv1i9hb.js");
3
3
  const https = require("node:https");
4
4
  const http = require("node:http");
5
5
  const xmldom = require("@xmldom/xmldom");
@@ -313,7 +313,7 @@ function registerSoapHandlers(ipc) {
313
313
  handle.handleIpc(ipc, handle.IPC.wsdl.import, async (_event, opts) => {
314
314
  const { validateWsdlImport } = await Promise.resolve().then(() => require("./ipc-validate-sqUFqRmE.js"));
315
315
  validateWsdlImport(opts);
316
- const { importWsdl } = await Promise.resolve().then(() => require("./import-B5T5wDu7.js"));
316
+ const { importWsdl } = await Promise.resolve().then(() => require("./import-DTcZMRNu.js"));
317
317
  const wsdlText = opts.xml ?? (opts.url ? await fetchUrl(opts.url) : "");
318
318
  if (!wsdlText) throw new Error("wsdl:import requires either `url` or `xml`");
319
319
  return importWsdl(wsdlText, { name: opts.name, existingMockPorts: opts.existingMockPorts });