@testsmith/api-spector 0.8.6 → 0.8.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/main/chunks/{broker-client-Bp9YId3a.js → broker-client-Bmt4seb8.js} +1 -1
- package/out/main/chunks/{perf-run-Ddxq_lFW.js → perf-run-wfdvpJev.js} +7 -5
- package/out/main/chunks/{request-collection-gkOYlWml.js → request-collection-zRRUfg0X.js} +35 -4
- package/out/main/chunks/{request-exec-BI4sXJop.js → request-exec-s7k-oRi2.js} +17 -50
- package/out/main/contract.js +2 -2
- package/out/main/flow.js +5 -5
- package/out/main/index.js +5 -5
- package/out/main/lib.js +1 -1
- package/out/main/perf-worker.js +4 -4
- package/out/main/perf.js +7 -7
- package/out/main/register-handlers.js +117 -37
- package/out/main/runner.js +51 -19
- package/out/renderer/assets/{GraphQLEditor-BwaRI4Ba.js → GraphQLEditor-TklsnkDw.js} +1 -1
- package/out/renderer/assets/{SoapEditor-DafZPr0j.js → SoapEditor-CllZqWyv.js} +1 -1
- package/out/renderer/assets/index-Dj6OhE3D.css +627 -0
- package/out/renderer/assets/{index-DTzdyIHp.js → index-fV0So_bF.js} +706 -369
- package/out/renderer/index.html +2 -2
- package/package.json +1 -1
- package/out/renderer/assets/index-DH2Ag-Ov.css +0 -627
|
@@ -29,13 +29,13 @@ const path = require("node:path");
|
|
|
29
29
|
const crypto = require("node:crypto");
|
|
30
30
|
const JSZip = require("jszip");
|
|
31
31
|
const dataFiles = require("./chunks/data-files-BYqhsoPe.js");
|
|
32
|
-
const
|
|
32
|
+
const requestCollection = require("./chunks/request-collection-zRRUfg0X.js");
|
|
33
|
+
const requestExec = require("./chunks/request-exec-s7k-oRi2.js");
|
|
33
34
|
const ipcValidate = require("./chunks/ipc-validate-sqUFqRmE.js");
|
|
34
35
|
const uuid = require("uuid");
|
|
35
36
|
const jsYaml = require("js-yaml");
|
|
36
37
|
const undici = require("undici");
|
|
37
38
|
const soapHandler = require("./chunks/soap-handler-BF5ii0UV.js");
|
|
38
|
-
const requestCollection = require("./chunks/request-collection-gkOYlWml.js");
|
|
39
39
|
const runConcurrent = require("./chunks/run-concurrent-468tH0AX.js");
|
|
40
40
|
const flowEngine = require("./chunks/flow-engine-BxwjEy37.js");
|
|
41
41
|
const mockServer = require("./chunks/mock-server-C4kTFkod.js");
|
|
@@ -49,7 +49,7 @@ const os = require("node:os");
|
|
|
49
49
|
const grpc = require("@grpc/grpc-js");
|
|
50
50
|
const protoLoader = require("@grpc/proto-loader");
|
|
51
51
|
const escape = require("./chunks/escape-DDVAlOvZ.js");
|
|
52
|
-
const brokerClient = require("./chunks/broker-client-
|
|
52
|
+
const brokerClient = require("./chunks/broker-client-Bmt4seb8.js");
|
|
53
53
|
const simpleGit = require("simple-git");
|
|
54
54
|
const recorder = require("./chunks/recorder-BkO8G_mB.js");
|
|
55
55
|
require("fs/promises");
|
|
@@ -297,7 +297,7 @@ Create a workspace in this folder and add "${path.basename(wsPath)}" to it? The
|
|
|
297
297
|
}
|
|
298
298
|
workspaceDir = path.dirname(wsPath);
|
|
299
299
|
workspaceFile = wsPath;
|
|
300
|
-
await
|
|
300
|
+
await requestCollection.loadGlobals(workspaceDir);
|
|
301
301
|
await saveLastWorkspacePath(wsPath);
|
|
302
302
|
requestExec.setSecretsConfig(parsed.settings?.secrets);
|
|
303
303
|
return { workspace: parsed, workspacePath: wsPath };
|
|
@@ -313,7 +313,7 @@ Create a workspace in this folder and add "${path.basename(wsPath)}" to it? The
|
|
|
313
313
|
if (result.canceled || !result.filePath) return null;
|
|
314
314
|
workspaceDir = path.dirname(result.filePath);
|
|
315
315
|
workspaceFile = result.filePath;
|
|
316
|
-
await
|
|
316
|
+
await requestCollection.loadGlobals(workspaceDir);
|
|
317
317
|
await promises.mkdir(path.join(workspaceDir, "collections"), { recursive: true });
|
|
318
318
|
await promises.mkdir(path.join(workspaceDir, "environments"), { recursive: true });
|
|
319
319
|
await promises.mkdir(path.join(workspaceDir, "flows"), { recursive: true });
|
|
@@ -420,10 +420,10 @@ Create a workspace in this folder and add "${path.basename(wsPath)}" to it? The
|
|
|
420
420
|
await promises.writeFile(result.filePath, content, "utf8");
|
|
421
421
|
return true;
|
|
422
422
|
});
|
|
423
|
-
handle.handleIpc(ipc, handle.IPC.globals.get, () =>
|
|
423
|
+
handle.handleIpc(ipc, handle.IPC.globals.get, () => requestCollection.getGlobals());
|
|
424
424
|
handle.handleIpc(ipc, handle.IPC.globals.set, async (_e, patch) => {
|
|
425
|
-
|
|
426
|
-
await
|
|
425
|
+
requestCollection.setGlobals(patch);
|
|
426
|
+
await requestCollection.persistGlobals();
|
|
427
427
|
});
|
|
428
428
|
handle.handleIpc(ipc, handle.IPC.file.closeWorkspace, async () => {
|
|
429
429
|
workspaceDir = null;
|
|
@@ -445,7 +445,7 @@ Create a workspace in this folder and add "${path.basename(wsPath)}" to it? The
|
|
|
445
445
|
const workspace = JSON.parse(raw);
|
|
446
446
|
workspaceDir = path.dirname(wsPath);
|
|
447
447
|
workspaceFile = wsPath;
|
|
448
|
-
await
|
|
448
|
+
await requestCollection.loadGlobals(workspaceDir);
|
|
449
449
|
requestExec.setSecretsConfig(workspace.settings?.secrets);
|
|
450
450
|
return { workspace, workspacePath: wsPath };
|
|
451
451
|
} catch {
|
|
@@ -475,7 +475,7 @@ Create a workspace in this folder and add "${path.basename(wsPath)}" to it? The
|
|
|
475
475
|
if (looksLikeCollection(parsed)) return null;
|
|
476
476
|
workspaceDir = path.dirname(wsPath);
|
|
477
477
|
workspaceFile = wsPath;
|
|
478
|
-
await
|
|
478
|
+
await requestCollection.loadGlobals(workspaceDir);
|
|
479
479
|
await saveLastWorkspacePath(wsPath);
|
|
480
480
|
requestExec.setSecretsConfig(parsed.settings?.secrets);
|
|
481
481
|
return { workspace: parsed, workspacePath: wsPath };
|
|
@@ -509,7 +509,7 @@ Create a workspace in this folder and add "${path.basename(wsPath)}" to it? The
|
|
|
509
509
|
const parsed = JSON.parse(await promises.readFile(wsPath, "utf8"));
|
|
510
510
|
workspaceDir = path.dirname(wsPath);
|
|
511
511
|
workspaceFile = wsPath;
|
|
512
|
-
await
|
|
512
|
+
await requestCollection.loadGlobals(workspaceDir);
|
|
513
513
|
await saveLastWorkspacePath(wsPath);
|
|
514
514
|
requestExec.setSecretsConfig(parsed.settings?.secrets);
|
|
515
515
|
return { workspace: parsed, workspacePath: wsPath };
|
|
@@ -530,7 +530,7 @@ async function tryOpenWorkspaceInDir(dir) {
|
|
|
530
530
|
const workspace = JSON.parse(raw);
|
|
531
531
|
workspaceDir = dir;
|
|
532
532
|
workspaceFile = wsPath;
|
|
533
|
-
await
|
|
533
|
+
await requestCollection.loadGlobals(workspaceDir);
|
|
534
534
|
requestExec.setSecretsConfig(workspace.settings?.secrets);
|
|
535
535
|
return { workspace, workspacePath: wsPath };
|
|
536
536
|
} catch {
|
|
@@ -657,10 +657,15 @@ async function wrapCollectionAsWorkspace(collFilePath, collection) {
|
|
|
657
657
|
await atomicWrite(collFilePath, JSON.stringify(ws, null, 2));
|
|
658
658
|
workspaceDir = dir;
|
|
659
659
|
workspaceFile = collFilePath;
|
|
660
|
-
await
|
|
660
|
+
await requestCollection.loadGlobals(dir);
|
|
661
661
|
await saveLastWorkspacePath(collFilePath);
|
|
662
662
|
return { workspace: ws, workspacePath: collFilePath };
|
|
663
663
|
}
|
|
664
|
+
function scopeDelta(base, updated) {
|
|
665
|
+
const out = {};
|
|
666
|
+
for (const [k, v] of Object.entries(updated)) if (base[k] !== v) out[k] = v;
|
|
667
|
+
return out;
|
|
668
|
+
}
|
|
664
669
|
function asObject(value) {
|
|
665
670
|
return value && typeof value === "object" ? value : null;
|
|
666
671
|
}
|
|
@@ -737,6 +742,7 @@ function registerRequestHandler(ipc) {
|
|
|
737
742
|
request: req,
|
|
738
743
|
environment,
|
|
739
744
|
collectionVars,
|
|
745
|
+
folderVars = {},
|
|
740
746
|
globals: payloadGlobals,
|
|
741
747
|
proxy,
|
|
742
748
|
tls,
|
|
@@ -759,7 +765,7 @@ function registerRequestHandler(ipc) {
|
|
|
759
765
|
flushTimer = setInterval(flushStream, 40);
|
|
760
766
|
}
|
|
761
767
|
const start = Date.now();
|
|
762
|
-
const liveGlobals =
|
|
768
|
+
const liveGlobals = requestCollection.getGlobals();
|
|
763
769
|
const mergedGlobals = { ...payloadGlobals, ...liveGlobals };
|
|
764
770
|
const envVars = await requestExec.buildEnvVars(environment);
|
|
765
771
|
let localVars = {};
|
|
@@ -777,31 +783,37 @@ function registerRequestHandler(ipc) {
|
|
|
777
783
|
}
|
|
778
784
|
const dynamicVars = await requestExec.buildDynamicVars();
|
|
779
785
|
if (dataRow && Object.keys(dataRow).length > 0) {
|
|
780
|
-
localVars = requestExec.resolveDataRow(dataRow, requestExec.mergeVars(envVars, collectionVars, mergedGlobals, {}, dynamicVars));
|
|
786
|
+
localVars = requestExec.resolveDataRow(dataRow, requestExec.mergeVars(envVars, collectionVars, mergedGlobals, {}, dynamicVars, folderVars));
|
|
781
787
|
}
|
|
782
788
|
if (req.variables && Object.keys(req.variables).length > 0) {
|
|
783
789
|
localVars = { ...req.variables, ...localVars };
|
|
784
790
|
}
|
|
785
|
-
|
|
791
|
+
const seededLocalVars = { ...localVars };
|
|
792
|
+
let vars = requestExec.mergeVars(envVars, collectionVars, mergedGlobals, localVars, dynamicVars, folderVars);
|
|
786
793
|
let preScriptMeta = { consoleOutput: [] };
|
|
787
794
|
let updatedCollectionVars = { ...collectionVars };
|
|
788
795
|
let updatedEnvVars = { ...envVars };
|
|
789
796
|
let updatedGlobals = { ...mergedGlobals };
|
|
797
|
+
let updatedFolderVars = { ...folderVars };
|
|
790
798
|
if (req.preRequestScript?.trim()) {
|
|
791
799
|
const result = await requestExec.runScript(requestExec.interpolate(req.preRequestScript, vars), {
|
|
792
800
|
envVars: { ...envVars },
|
|
793
801
|
collectionVars: { ...collectionVars },
|
|
794
802
|
globals: { ...mergedGlobals },
|
|
795
|
-
|
|
803
|
+
folderVars: { ...folderVars },
|
|
804
|
+
// Seed the script's local scope with the already-resolved local vars
|
|
805
|
+
// (request-scoped variables + any data-row) so they survive the script
|
|
806
|
+
// run — otherwise updatedLocalVars below would overwrite them with {} and
|
|
807
|
+
// {{request-level vars}} would go un-interpolated at send time.
|
|
808
|
+
localVars: { ...localVars }
|
|
796
809
|
});
|
|
797
810
|
preScriptMeta = { error: result.error, consoleOutput: result.consoleOutput };
|
|
798
811
|
localVars = result.updatedLocalVars;
|
|
799
812
|
updatedEnvVars = result.updatedEnvVars;
|
|
800
813
|
updatedCollectionVars = result.updatedCollectionVars;
|
|
801
814
|
updatedGlobals = result.updatedGlobals;
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
vars = requestExec.mergeVars(updatedEnvVars, updatedCollectionVars, updatedGlobals, localVars, dynamicVars);
|
|
815
|
+
updatedFolderVars = result.updatedFolderVars;
|
|
816
|
+
vars = requestExec.mergeVars(updatedEnvVars, updatedCollectionVars, updatedGlobals, localVars, dynamicVars, updatedFolderVars);
|
|
805
817
|
}
|
|
806
818
|
let response;
|
|
807
819
|
let scriptResponse;
|
|
@@ -913,6 +925,7 @@ function registerRequestHandler(ipc) {
|
|
|
913
925
|
envVars: { ...updatedEnvVars },
|
|
914
926
|
collectionVars: { ...updatedCollectionVars },
|
|
915
927
|
globals: { ...updatedGlobals },
|
|
928
|
+
folderVars: { ...updatedFolderVars },
|
|
916
929
|
localVars: { ...localVars },
|
|
917
930
|
// Pass the *unmasked* response so the script can extract real values
|
|
918
931
|
// (tokens, ids, …). The displayed `response` keeps the redacted copy.
|
|
@@ -924,9 +937,8 @@ function registerRequestHandler(ipc) {
|
|
|
924
937
|
updatedEnvVars = result.updatedEnvVars;
|
|
925
938
|
updatedCollectionVars = result.updatedCollectionVars;
|
|
926
939
|
updatedGlobals = result.updatedGlobals;
|
|
940
|
+
updatedFolderVars = result.updatedFolderVars;
|
|
927
941
|
localVars = result.updatedLocalVars;
|
|
928
|
-
requestExec.patchGlobals(result.updatedGlobals);
|
|
929
|
-
await requestExec.persistGlobals();
|
|
930
942
|
}
|
|
931
943
|
const combinedTestResults = [...schemaTestResults, ...postTestResults];
|
|
932
944
|
if (!response.error && response.status >= 400 && combinedTestResults.length === 0) {
|
|
@@ -939,10 +951,14 @@ function registerRequestHandler(ipc) {
|
|
|
939
951
|
const scriptResult = {
|
|
940
952
|
testResults: combinedTestResults,
|
|
941
953
|
consoleOutput: [...decryptionWarnings, ...preScriptMeta.consoleOutput, ...postConsole],
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
954
|
+
// Persist only what the scripts actually changed, so echoed inputs (the
|
|
955
|
+
// sent session/collection vars, the seeded request/data vars) don't get
|
|
956
|
+
// written back and overwrite stored values.
|
|
957
|
+
updatedEnvVars: scopeDelta(envVars, updatedEnvVars),
|
|
958
|
+
updatedCollectionVars: scopeDelta(collectionVars, updatedCollectionVars),
|
|
959
|
+
updatedGlobals: scopeDelta(mergedGlobals, updatedGlobals),
|
|
960
|
+
updatedLocalVars: scopeDelta(seededLocalVars, localVars),
|
|
961
|
+
updatedFolderVars: scopeDelta(folderVars, updatedFolderVars),
|
|
946
962
|
resolvedUrl,
|
|
947
963
|
preScriptError: preScriptMeta.error,
|
|
948
964
|
postScriptError: postError
|
|
@@ -952,8 +968,6 @@ function registerRequestHandler(ipc) {
|
|
|
952
968
|
handle.handleIpc(ipc, handle.IPC.script.runHook, async (_e, payload) => {
|
|
953
969
|
const { script, envVars, collectionVars, globals } = payload;
|
|
954
970
|
const result = await requestExec.runScript(script, { envVars, collectionVars, globals, localVars: {} });
|
|
955
|
-
requestExec.patchGlobals(result.updatedGlobals);
|
|
956
|
-
await requestExec.persistGlobals();
|
|
957
971
|
return {
|
|
958
972
|
updatedEnvVars: result.updatedEnvVars,
|
|
959
973
|
updatedCollectionVars: result.updatedCollectionVars,
|
|
@@ -1124,6 +1138,18 @@ function parseHeaders$1(raw) {
|
|
|
1124
1138
|
description: h.description ?? ""
|
|
1125
1139
|
}));
|
|
1126
1140
|
}
|
|
1141
|
+
function parseCollectionVariables(raw) {
|
|
1142
|
+
const variables = {};
|
|
1143
|
+
const disabled = {};
|
|
1144
|
+
for (const v of raw ?? []) {
|
|
1145
|
+
const key = typeof v?.key === "string" ? v.key.trim() : "";
|
|
1146
|
+
if (!key) continue;
|
|
1147
|
+
const value = v.value == null ? "" : String(v.value);
|
|
1148
|
+
if (v.disabled) disabled[key] = value;
|
|
1149
|
+
else variables[key] = value;
|
|
1150
|
+
}
|
|
1151
|
+
return { variables, disabled };
|
|
1152
|
+
}
|
|
1127
1153
|
function parseParams$1(urlObj) {
|
|
1128
1154
|
if (!urlObj || typeof urlObj === "string") return [];
|
|
1129
1155
|
return (urlObj.query ?? []).map((p) => ({
|
|
@@ -1305,13 +1331,16 @@ async function importPostman(filePath) {
|
|
|
1305
1331
|
rootFolder.requestIds.push(req.id);
|
|
1306
1332
|
}
|
|
1307
1333
|
}
|
|
1334
|
+
const { variables: collectionVariables, disabled: collectionVariablesDisabled } = parseCollectionVariables(data.variable);
|
|
1308
1335
|
return {
|
|
1309
1336
|
version: "1.0",
|
|
1310
1337
|
id: uuid.v4(),
|
|
1311
1338
|
name: info.name ?? "Imported Collection",
|
|
1312
1339
|
description: info.description ?? "",
|
|
1313
1340
|
rootFolder,
|
|
1314
|
-
requests
|
|
1341
|
+
requests,
|
|
1342
|
+
...Object.keys(collectionVariables).length ? { collectionVariables } : {},
|
|
1343
|
+
...Object.keys(collectionVariablesDisabled).length ? { collectionVariablesDisabled } : {}
|
|
1315
1344
|
};
|
|
1316
1345
|
}
|
|
1317
1346
|
async function importPostmanEnvironment(filePath) {
|
|
@@ -1741,6 +1770,15 @@ function parseAuth(auth) {
|
|
|
1741
1770
|
}
|
|
1742
1771
|
return { type: "none" };
|
|
1743
1772
|
}
|
|
1773
|
+
function toVarMap(data) {
|
|
1774
|
+
const out = {};
|
|
1775
|
+
if (!data || typeof data !== "object") return out;
|
|
1776
|
+
for (const [key, value] of Object.entries(data)) {
|
|
1777
|
+
if (!key || value == null) continue;
|
|
1778
|
+
out[key] = typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
1779
|
+
}
|
|
1780
|
+
return out;
|
|
1781
|
+
}
|
|
1744
1782
|
async function importInsomnia(filePath) {
|
|
1745
1783
|
const raw = await promises.readFile(filePath, "utf8");
|
|
1746
1784
|
const data = JSON.parse(raw);
|
|
@@ -1752,12 +1790,14 @@ async function importInsomnia(filePath) {
|
|
|
1752
1790
|
const rootFolder = { id: uuid.v4(), name: "root", description: "", folders: [], requestIds: [] };
|
|
1753
1791
|
for (const r of resources) {
|
|
1754
1792
|
if (r._type !== "request_group") continue;
|
|
1793
|
+
const variables = toVarMap(r.environment);
|
|
1755
1794
|
folderById[r._id] = {
|
|
1756
1795
|
id: uuid.v4(),
|
|
1757
1796
|
name: r.name ?? "Folder",
|
|
1758
1797
|
description: r.description ?? "",
|
|
1759
1798
|
folders: [],
|
|
1760
|
-
requestIds: []
|
|
1799
|
+
requestIds: [],
|
|
1800
|
+
...Object.keys(variables).length ? { variables } : {}
|
|
1761
1801
|
};
|
|
1762
1802
|
}
|
|
1763
1803
|
for (const r of resources) {
|
|
@@ -1794,13 +1834,16 @@ async function importInsomnia(filePath) {
|
|
|
1794
1834
|
folderById[r.parentId].folders.push(folder);
|
|
1795
1835
|
}
|
|
1796
1836
|
}
|
|
1837
|
+
const baseEnv = resources.find((r) => r._type === "environment" && r.parentId === workspaceId) ?? resources.find((r) => r._type === "environment");
|
|
1838
|
+
const collectionVariables = toVarMap(baseEnv?.data);
|
|
1797
1839
|
return {
|
|
1798
1840
|
version: "1.0",
|
|
1799
1841
|
id: uuid.v4(),
|
|
1800
1842
|
name: workspace.name ?? "Imported Collection",
|
|
1801
1843
|
description: workspace.description ?? "",
|
|
1802
1844
|
rootFolder,
|
|
1803
|
-
requests
|
|
1845
|
+
requests,
|
|
1846
|
+
...Object.keys(collectionVariables).length ? { collectionVariables } : {}
|
|
1804
1847
|
};
|
|
1805
1848
|
}
|
|
1806
1849
|
function extractBlock(content, blockName) {
|
|
@@ -1840,6 +1883,20 @@ function parseKv(blockContent) {
|
|
|
1840
1883
|
}
|
|
1841
1884
|
return result;
|
|
1842
1885
|
}
|
|
1886
|
+
function parseVarsBlock(blockContent) {
|
|
1887
|
+
const variables = {};
|
|
1888
|
+
const disabled = {};
|
|
1889
|
+
for (const [rawKey, value] of Object.entries(parseKv(blockContent))) {
|
|
1890
|
+
if (rawKey.startsWith("~")) {
|
|
1891
|
+
const key = rawKey.slice(1).trim();
|
|
1892
|
+
if (key) disabled[key] = value;
|
|
1893
|
+
} else {
|
|
1894
|
+
variables[rawKey] = value;
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
return { variables, disabled };
|
|
1898
|
+
}
|
|
1899
|
+
const BRU_CONFIG_FILES = /* @__PURE__ */ new Set(["folder.bru", "collection.bru"]);
|
|
1843
1900
|
const HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options", "query"];
|
|
1844
1901
|
function parseBruFile(content, fileName) {
|
|
1845
1902
|
const meta = parseKv(extractBlock(content, "meta"));
|
|
@@ -1904,6 +1961,7 @@ function parseBruFile(content, fileName) {
|
|
|
1904
1961
|
apiKeyIn: apiKeyAttrs["in"] === "query" ? "query" : "header"
|
|
1905
1962
|
};
|
|
1906
1963
|
}
|
|
1964
|
+
const { variables, disabled: variablesDisabled } = parseVarsBlock(extractBlock(content, "vars:pre-request"));
|
|
1907
1965
|
return {
|
|
1908
1966
|
id: uuid.v4(),
|
|
1909
1967
|
name: meta["name"] ?? path.basename(fileName, ".bru"),
|
|
@@ -1917,7 +1975,9 @@ function parseBruFile(content, fileName) {
|
|
|
1917
1975
|
description: extractBlock(content, "docs")?.trim() ?? "",
|
|
1918
1976
|
preRequestScript: extractBlock(content, "script:pre-request") ? translateScript(extractBlock(content, "script:pre-request").trim(), "bruno") || void 0 : void 0,
|
|
1919
1977
|
postRequestScript: extractBlock(content, "script:post-response") ? translateScript(extractBlock(content, "script:post-response").trim(), "bruno") || void 0 : void 0,
|
|
1920
|
-
meta: { seq: meta["seq"] !== void 0 ? Number(meta["seq"]) : 0 }
|
|
1978
|
+
meta: { seq: meta["seq"] !== void 0 ? Number(meta["seq"]) : 0 },
|
|
1979
|
+
...Object.keys(variables).length ? { variables } : {},
|
|
1980
|
+
...Object.keys(variablesDisabled).length ? { variablesDisabled } : {}
|
|
1921
1981
|
};
|
|
1922
1982
|
}
|
|
1923
1983
|
async function scanDir(dirPath, name, requests, ignore) {
|
|
@@ -1931,7 +1991,16 @@ async function scanDir(dirPath, name, requests, ignore) {
|
|
|
1931
1991
|
if (s.isDirectory()) {
|
|
1932
1992
|
const sub = await scanDir(fullPath, entry, requests, ignore);
|
|
1933
1993
|
if (sub.folders.length > 0 || sub.requestIds.length > 0) folder.folders.push(sub);
|
|
1934
|
-
} else if (entry
|
|
1994
|
+
} else if (entry === "folder.bru") {
|
|
1995
|
+
const content = await promises.readFile(fullPath, "utf8");
|
|
1996
|
+
const meta = parseKv(extractBlock(content, "meta"));
|
|
1997
|
+
if (meta["name"]) folder.name = meta["name"];
|
|
1998
|
+
const docs = extractBlock(content, "docs")?.trim();
|
|
1999
|
+
if (docs) folder.description = docs;
|
|
2000
|
+
const { variables, disabled } = parseVarsBlock(extractBlock(content, "vars:pre-request"));
|
|
2001
|
+
if (Object.keys(variables).length) folder.variables = variables;
|
|
2002
|
+
if (Object.keys(disabled).length) folder.variablesDisabled = disabled;
|
|
2003
|
+
} else if (entry.endsWith(".bru") && !BRU_CONFIG_FILES.has(entry)) {
|
|
1935
2004
|
const content = await promises.readFile(fullPath, "utf8");
|
|
1936
2005
|
const req = parseBruFile(content, entry);
|
|
1937
2006
|
requests[req.id] = req;
|
|
@@ -1949,13 +2018,24 @@ async function importBruno(filePath) {
|
|
|
1949
2018
|
const ignore = /* @__PURE__ */ new Set(["node_modules", ".git", ...config.ignore ?? []]);
|
|
1950
2019
|
const requests = {};
|
|
1951
2020
|
const rootFolder = await scanDir(dirPath, "root", requests, ignore);
|
|
2021
|
+
let collectionVariables = {};
|
|
2022
|
+
let collectionVariablesDisabled = {};
|
|
2023
|
+
try {
|
|
2024
|
+
const colBru = await promises.readFile(path.join(dirPath, "collection.bru"), "utf8");
|
|
2025
|
+
const parsed = parseVarsBlock(extractBlock(colBru, "vars:pre-request"));
|
|
2026
|
+
collectionVariables = parsed.variables;
|
|
2027
|
+
collectionVariablesDisabled = parsed.disabled;
|
|
2028
|
+
} catch {
|
|
2029
|
+
}
|
|
1952
2030
|
return {
|
|
1953
2031
|
version: "1.0",
|
|
1954
2032
|
id: uuid.v4(),
|
|
1955
2033
|
name: config.name ?? "Imported Collection",
|
|
1956
2034
|
description: "",
|
|
1957
2035
|
rootFolder,
|
|
1958
|
-
requests
|
|
2036
|
+
requests,
|
|
2037
|
+
...Object.keys(collectionVariables).length ? { collectionVariables } : {},
|
|
2038
|
+
...Object.keys(collectionVariablesDisabled).length ? { collectionVariablesDisabled } : {}
|
|
1959
2039
|
};
|
|
1960
2040
|
}
|
|
1961
2041
|
const METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "QUERY"];
|
|
@@ -5355,7 +5435,7 @@ function registerRunnerHandler(ipc) {
|
|
|
5355
5435
|
const { items, environment, globals: payloadGlobals, proxy, tls, piiMaskPatterns = [], requestDelay = 0 } = payload;
|
|
5356
5436
|
const concurrency = Math.max(1, Math.min(20, Math.floor(payload.concurrency ?? 1)));
|
|
5357
5437
|
const envVars = await requestExec.buildEnvVars(environment);
|
|
5358
|
-
const liveGlobals =
|
|
5438
|
+
const liveGlobals = requestCollection.getGlobals();
|
|
5359
5439
|
const globals = { ...payloadGlobals, ...liveGlobals };
|
|
5360
5440
|
const dispatcher = await requestExec.buildDispatcher(proxy, tls);
|
|
5361
5441
|
const summary = { total: items.length, passed: 0, failed: 0, errors: 0, skipped: 0, durationMs: 0 };
|
|
@@ -5480,7 +5560,7 @@ function registerFlowHandler(ipc) {
|
|
|
5480
5560
|
const flow = flowEngine.normalizeFlow(payload.flow);
|
|
5481
5561
|
const flows = (payload.flows ?? []).map(flowEngine.normalizeFlow);
|
|
5482
5562
|
const envVars = await requestExec.buildEnvVars(environment);
|
|
5483
|
-
const globals = { ...payloadGlobals, ...
|
|
5563
|
+
const globals = { ...payloadGlobals, ...requestCollection.getGlobals() };
|
|
5484
5564
|
const dispatcher = await requestExec.buildDispatcher(proxy, tls);
|
|
5485
5565
|
const colById = new Map(collections.map((c) => [c.id, c]));
|
|
5486
5566
|
const deps = {
|
|
@@ -7829,7 +7909,7 @@ function registerCloudHandlers(ipc) {
|
|
|
7829
7909
|
handle.handleIpc(ipc, handle.IPC.cloud.pushMonitor, async (_e, input) => {
|
|
7830
7910
|
const { request, environment, collectionVars, globals } = input;
|
|
7831
7911
|
const envVars = await requestExec.buildEnvVars(environment);
|
|
7832
|
-
const liveGlobals = { ...globals, ...
|
|
7912
|
+
const liveGlobals = { ...globals, ...requestCollection.getGlobals() };
|
|
7833
7913
|
const staticVars = requestExec.mergeVars(envVars, collectionVars ?? {}, liveGlobals, {}, {});
|
|
7834
7914
|
const setup = input.setup ?? [];
|
|
7835
7915
|
const resolveVars2 = omitKeys(staticVars, setVarNames(setup));
|
package/out/main/runner.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
const promises = require("node:fs/promises");
|
|
4
4
|
const path = require("node:path");
|
|
5
|
-
const requestExec = require("./chunks/request-exec-
|
|
5
|
+
const requestExec = require("./chunks/request-exec-s7k-oRi2.js");
|
|
6
|
+
const requestCollection = require("./chunks/request-collection-zRRUfg0X.js");
|
|
6
7
|
const escape = require("./chunks/escape-DDVAlOvZ.js");
|
|
7
|
-
const requestCollection = require("./chunks/request-collection-gkOYlWml.js");
|
|
8
8
|
const runConcurrent = require("./chunks/run-concurrent-468tH0AX.js");
|
|
9
9
|
const environments = require("./chunks/environments-iM3SUM-4.js");
|
|
10
10
|
const cliCommon = require("./chunks/cli-common-GnihVSjU.js");
|
|
@@ -510,10 +510,10 @@ function printResult(r, verbose) {
|
|
|
510
510
|
if (r.error) console.log(cliCommon.color(` Error: ${r.error}`, cliCommon.C.red));
|
|
511
511
|
}
|
|
512
512
|
async function main() {
|
|
513
|
-
const args = cliCommon.parseArgs(process.argv.slice(2));
|
|
513
|
+
const args = cliCommon.parseArgs(process.argv.slice(2), ["ask", "var", "env-var", "global-var"]);
|
|
514
514
|
if (args.help) {
|
|
515
515
|
console.log(
|
|
516
|
-
'\nUsage:\n api-spector run --workspace <path> [--environment <name>] [--tags <a,b>]\n [--collection <name>] [--output <path>] [--format json|junit|html|sarif]\n [--ask "Message=value"] [--concurrency <n>] [--verbose] [--bail]\n\n --ask "Message=value" Supply an ask variable ({{@Message}}) for this run\n (repeatable). Requests whose ask variables are not\n supplied are skipped.\n --concurrency <n> Run n requests at a time (default 1 = sequential).\n Hooks run as barriers; best for independent requests.\n'
|
|
516
|
+
'\nUsage:\n api-spector run --workspace <path> [--environment <name>] [--tags <a,b>]\n [--collection <name>] [--var "key=value"]\n [--env-var "key=value"] [--global-var "key=value"]\n [--output <path>] [--format json|junit|html|sarif]\n [--ask "Message=value"] [--concurrency <n>] [--verbose] [--bail]\n\n --var "key=value" Override a variable for this run (repeatable). Sits at\n the CLI tier: above environment/collection/folder/\n request values, below data-row and script-set values.\n --env-var "key=value" Set an environment variable for this run (repeatable).\n Seeds the environment scope; a script may override it.\n --global-var "key=value" Set a global variable for this run (repeatable).\n Seeds the globals scope; a script may override it.\n --ask "Message=value" Supply an ask variable ({{@Message}}) for this run\n (repeatable). Requests whose ask variables are not\n supplied are skipped.\n --concurrency <n> Run n requests at a time (default 1 = sequential).\n Hooks run as barriers; best for independent requests.\n'
|
|
517
517
|
);
|
|
518
518
|
process.exit(0);
|
|
519
519
|
}
|
|
@@ -539,7 +539,7 @@ async function main() {
|
|
|
539
539
|
console.error(cliCommon.color(`Error: could not read workspace file: ${wsPath}`, cliCommon.C.red));
|
|
540
540
|
process.exit(1);
|
|
541
541
|
}
|
|
542
|
-
await
|
|
542
|
+
await requestCollection.loadGlobals(wsDir);
|
|
543
543
|
requestExec.setSecretsConfig(workspace.settings?.secrets);
|
|
544
544
|
const collections = await cliCommon.loadCollections(workspace, wsDir, {
|
|
545
545
|
onError: (relPath) => console.error(cliCommon.color(` [warn] Could not load collection: ${relPath}`, cliCommon.C.yellow))
|
|
@@ -551,7 +551,7 @@ async function main() {
|
|
|
551
551
|
} else if (!envName && workspace.settings?.defaultEnvironment && !env) {
|
|
552
552
|
console.warn(cliCommon.color(`Warning: default environment "${workspace.settings.defaultEnvironment}" not found. Running without environment.`, cliCommon.C.yellow));
|
|
553
553
|
}
|
|
554
|
-
const version = `v${"0.8.
|
|
554
|
+
const version = `v${"0.8.8"}`;
|
|
555
555
|
console.log("");
|
|
556
556
|
console.log(cliCommon.color(" API Test Runner" + (version ? ` ${version}` : ""), cliCommon.C.bold, cliCommon.C.white));
|
|
557
557
|
console.log(cliCommon.color(` Workspace: ${wsPath}`, cliCommon.C.gray));
|
|
@@ -565,6 +565,18 @@ async function main() {
|
|
|
565
565
|
const eq = entry.indexOf("=");
|
|
566
566
|
if (eq > 0) askAnswers[entry.slice(0, eq).trim()] = entry.slice(eq + 1);
|
|
567
567
|
}
|
|
568
|
+
const parseKeyVals = (raw) => {
|
|
569
|
+
const out = {};
|
|
570
|
+
for (const entry of Array.isArray(raw) ? raw : raw ? [raw] : []) {
|
|
571
|
+
if (typeof entry !== "string") continue;
|
|
572
|
+
const eq = entry.indexOf("=");
|
|
573
|
+
if (eq > 0) out[entry.slice(0, eq).trim()] = entry.slice(eq + 1);
|
|
574
|
+
}
|
|
575
|
+
return out;
|
|
576
|
+
};
|
|
577
|
+
const cliVars = parseKeyVals(args["var"]);
|
|
578
|
+
const envVarOverrides = parseKeyVals(args["env-var"]);
|
|
579
|
+
const globalVarOverrides = parseKeyVals(args["global-var"]);
|
|
568
580
|
const concurrency = Math.max(1, Math.min(20, Math.floor(Number(args["concurrency"] ?? 1) || 1)));
|
|
569
581
|
const envVarsSnapshot = await requestExec.buildEnvVars(env);
|
|
570
582
|
const secretValuesToMask = (env?.variables ?? []).filter((v) => (v.secret || v.secretRef) && v.enabled).map((v) => envVarsSnapshot[v.key]).filter((v) => typeof v === "string" && v.length > 0);
|
|
@@ -616,11 +628,26 @@ async function main() {
|
|
|
616
628
|
let items = requestCollection.buildRunPlan(col, null, filterTags);
|
|
617
629
|
if (items.length === 0) continue;
|
|
618
630
|
if (!firstColName) firstColName = col.name;
|
|
619
|
-
let runEnvVars = await requestExec.buildEnvVars(env);
|
|
620
|
-
let runGlobals =
|
|
631
|
+
let runEnvVars = { ...await requestExec.buildEnvVars(env), ...envVarOverrides };
|
|
632
|
+
let runGlobals = { ...requestCollection.getGlobals(), ...globalVarOverrides };
|
|
621
633
|
let runCollectionVars = { ...col.collectionVariables };
|
|
622
634
|
let runLocalVars = {};
|
|
623
635
|
const runFolderVars = {};
|
|
636
|
+
const cliOverridden = /* @__PURE__ */ new Set();
|
|
637
|
+
const cliSeed = () => Object.fromEntries(Object.entries(cliVars).filter(([k]) => !cliOverridden.has(k)));
|
|
638
|
+
const stripCliSeed = (m) => {
|
|
639
|
+
const copy = { ...m };
|
|
640
|
+
for (const k of Object.keys(cliVars)) delete copy[k];
|
|
641
|
+
return copy;
|
|
642
|
+
};
|
|
643
|
+
const trackCliOverrides = (out, inputs) => {
|
|
644
|
+
for (const k of Object.keys(cliVars)) {
|
|
645
|
+
if (cliOverridden.has(k)) continue;
|
|
646
|
+
if (out.updatedCollectionVars[k] !== inputs.col[k] || out.updatedEnvVars[k] !== inputs.env[k] || out.updatedGlobals[k] !== inputs.globals[k] || out.updatedFolderVars[k] !== inputs.folder[k] || out.updatedLocalVars[k] !== inputs.local[k]) {
|
|
647
|
+
cliOverridden.add(k);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
};
|
|
624
651
|
console.log(cliCommon.color(` ┌ ${col.name}`, cliCommon.C.bold, cliCommon.C.white));
|
|
625
652
|
const workspaceTls = workspace.settings?.tls;
|
|
626
653
|
const effectiveTls = col.tls ? { ...workspaceTls, ...col.tls } : workspaceTls;
|
|
@@ -682,16 +709,16 @@ async function main() {
|
|
|
682
709
|
iterationLabel: item.iterationLabel
|
|
683
710
|
};
|
|
684
711
|
} else {
|
|
712
|
+
const colInput = { ...item.collectionVars, ...runCollectionVars, ...resolveAskValues(itemAsks, askAnswers) };
|
|
713
|
+
const folderInput = { ...item.folderVars ?? {}, ...item.folderId ? runFolderVars[item.folderId] ?? {} : {} };
|
|
714
|
+
const localInput = { ...cliSeed(), ...runLocalVars, ...item.dataRow };
|
|
685
715
|
const out = await requestExec.executeRunnerRequest({
|
|
686
716
|
req: item.request,
|
|
687
|
-
collectionVars:
|
|
717
|
+
collectionVars: colInput,
|
|
688
718
|
envVars: runEnvVars,
|
|
689
719
|
globals: runGlobals,
|
|
690
|
-
localVars:
|
|
691
|
-
folderVars:
|
|
692
|
-
...item.folderVars ?? {},
|
|
693
|
-
...item.folderId ? runFolderVars[item.folderId] ?? {} : {}
|
|
694
|
-
},
|
|
720
|
+
localVars: localInput,
|
|
721
|
+
folderVars: folderInput,
|
|
695
722
|
dispatcher,
|
|
696
723
|
piiMaskPatterns: piiPatterns,
|
|
697
724
|
tls: effectiveTls,
|
|
@@ -699,11 +726,12 @@ async function main() {
|
|
|
699
726
|
expectStatus: item.expectStatus,
|
|
700
727
|
owaspTag: item.owaspTag
|
|
701
728
|
});
|
|
729
|
+
trackCliOverrides(out, { col: colInput, env: runEnvVars, globals: runGlobals, folder: folderInput, local: localInput });
|
|
702
730
|
result = out.result;
|
|
703
731
|
runEnvVars = out.updatedEnvVars;
|
|
704
732
|
runCollectionVars = out.updatedCollectionVars;
|
|
705
733
|
runGlobals = out.updatedGlobals;
|
|
706
|
-
runLocalVars = out.updatedLocalVars;
|
|
734
|
+
runLocalVars = stripCliSeed(out.updatedLocalVars);
|
|
707
735
|
if (item.folderId) {
|
|
708
736
|
runFolderVars[item.folderId] = { ...runFolderVars[item.folderId] ?? {}, ...out.updatedFolderVars };
|
|
709
737
|
}
|
|
@@ -762,23 +790,27 @@ async function main() {
|
|
|
762
790
|
return;
|
|
763
791
|
}
|
|
764
792
|
try {
|
|
793
|
+
const colInput = { ...item.collectionVars, ...runCollectionVars, ...resolveAskValues(itemAsks, askAnswers) };
|
|
794
|
+
const folderInput = { ...item.folderVars ?? {}, ...item.folderId ? runFolderVars[item.folderId] ?? {} : {} };
|
|
795
|
+
const localInput = { ...cliSeed(), ...runLocalVars, ...item.dataRow };
|
|
765
796
|
const out = await requestExec.executeRunnerRequest({
|
|
766
797
|
req: item.request,
|
|
767
|
-
collectionVars:
|
|
798
|
+
collectionVars: colInput,
|
|
768
799
|
envVars: runEnvVars,
|
|
769
800
|
globals: runGlobals,
|
|
770
|
-
localVars:
|
|
771
|
-
folderVars:
|
|
801
|
+
localVars: localInput,
|
|
802
|
+
folderVars: folderInput,
|
|
772
803
|
dispatcher,
|
|
773
804
|
piiMaskPatterns: piiPatterns,
|
|
774
805
|
tls: effectiveTls,
|
|
775
806
|
expectStatus: item.expectStatus,
|
|
776
807
|
owaspTag: item.owaspTag
|
|
777
808
|
});
|
|
809
|
+
trackCliOverrides(out, { col: colInput, env: runEnvVars, globals: runGlobals, folder: folderInput, local: localInput });
|
|
778
810
|
runEnvVars = { ...runEnvVars, ...out.updatedEnvVars };
|
|
779
811
|
runCollectionVars = { ...runCollectionVars, ...out.updatedCollectionVars };
|
|
780
812
|
runGlobals = { ...runGlobals, ...out.updatedGlobals };
|
|
781
|
-
runLocalVars = { ...runLocalVars, ...out.updatedLocalVars };
|
|
813
|
+
runLocalVars = stripCliSeed({ ...runLocalVars, ...out.updatedLocalVars });
|
|
782
814
|
if (item.folderId) runFolderVars[item.folderId] = { ...runFolderVars[item.folderId] ?? {}, ...out.updatedFolderVars };
|
|
783
815
|
const result = out.result;
|
|
784
816
|
result.isHook = item.isHook;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { l as linter, E as EditorView, L as LanguageSupport, i as LRLanguage, s as styleTags, m as indentNodeProp, n as delimitedIndent, p as foldNodeProp, q as foldInside, S as StateField, u as LRParser, v as StateEffect, w as tags, a as reactExports, j as jsxRuntimeExports, e as ReactCodeMirror, o as oneDark, d as json } from "./codemirror-BKmwMUlV.js";
|
|
2
|
-
import { u as useT, a as useStore, b as useActiveEnvironment, i as isSubscriptionQuery } from "./index-
|
|
2
|
+
import { u as useT, a as useStore, b as useActiveEnvironment, i as isSubscriptionQuery } from "./index-fV0So_bF.js";
|
|
3
3
|
import "./ajv-CJPwFjiP.js";
|
|
4
4
|
function devAssert(condition, message) {
|
|
5
5
|
const booleanCondition = Boolean(condition);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as reactExports, j as jsxRuntimeExports, e as ReactCodeMirror, o as oneDark, x as xml } from "./codemirror-BKmwMUlV.js";
|
|
2
|
-
import { u as useT, c as btnPrimaryCls, d as commentKeymap } from "./index-
|
|
2
|
+
import { u as useT, c as btnPrimaryCls, d as commentKeymap } from "./index-fV0So_bF.js";
|
|
3
3
|
import "./ajv-CJPwFjiP.js";
|
|
4
4
|
const SOAP_11_CONTENT_TYPE = "text/xml; charset=utf-8";
|
|
5
5
|
const SOAP_12_CONTENT_TYPE = "application/soap+xml; charset=utf-8";
|