@testsmith/api-spector 0.4.6 → 0.4.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/agents.js +2 -1
- package/out/main/chunks/{cli-common-CDQY1erJ.js → cli-common-BKYgsbGB.js} +1 -0
- package/out/main/chunks/{handle-BGYDylL2.js → handle-rimXXdJH.js} +26 -1
- package/out/main/chunks/{import-CUcjlmSK.js → import-DcenB5Q_.js} +2 -2
- package/out/main/chunks/{request-exec-CBi2kL6s.js → request-exec-BH-M3KqZ.js} +201 -6
- package/out/main/chunks/{snapshots-BDe-B5iQ.js → snapshots-CtYxkSLz.js} +556 -6
- package/out/main/chunks/{soap-handler-CAyUjzxB.js → soap-handler-BXx842MN.js} +2 -2
- package/out/main/contract.js +265 -296
- package/out/main/index.js +365 -19
- package/out/main/lib.js +2 -2
- package/out/main/mock.js +1 -1
- package/out/main/record.js +1 -1
- package/out/main/runner.js +4 -4
- package/out/main/wsdl.js +5 -4
- package/out/preload/index.js +45 -1
- package/out/renderer/assets/index-B_xyaTDo.css +2 -0
- package/out/renderer/assets/{index-B55aZQcN.js → index-CEKgq6t0.js} +2609 -787
- package/out/renderer/index.html +2 -2
- package/package.json +8 -5
- package/out/renderer/assets/index-DkZ0bxof.css +0 -2
package/out/main/index.js
CHANGED
|
@@ -22,12 +22,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
22
22
|
mod
|
|
23
23
|
));
|
|
24
24
|
const electron = require("electron");
|
|
25
|
-
const handle = require("./chunks/handle-
|
|
25
|
+
const handle = require("./chunks/handle-rimXXdJH.js");
|
|
26
26
|
const path = require("path");
|
|
27
27
|
const fs = require("fs");
|
|
28
28
|
const promises = require("fs/promises");
|
|
29
29
|
const crypto = require("crypto");
|
|
30
|
-
const requestExec = require("./chunks/request-exec-
|
|
30
|
+
const requestExec = require("./chunks/request-exec-BH-M3KqZ.js");
|
|
31
31
|
const ipcValidate = require("./chunks/ipc-validate-k6KI8adf.js");
|
|
32
32
|
const uuid = require("uuid");
|
|
33
33
|
const jsYaml = require("js-yaml");
|
|
@@ -37,9 +37,9 @@ const requestCollection = require("./chunks/request-collection-CJoXpvOw.js");
|
|
|
37
37
|
const mockServer = require("./chunks/mock-server-BxiXhP1m.js");
|
|
38
38
|
const http = require("http");
|
|
39
39
|
const WebSocket = require("ws");
|
|
40
|
-
const soapHandler = require("./chunks/soap-handler-
|
|
40
|
+
const soapHandler = require("./chunks/soap-handler-BXx842MN.js");
|
|
41
41
|
const os = require("os");
|
|
42
|
-
const snapshots = require("./chunks/snapshots-
|
|
42
|
+
const snapshots = require("./chunks/snapshots-CtYxkSLz.js");
|
|
43
43
|
const simpleGit = require("simple-git");
|
|
44
44
|
const recorder = require("./chunks/recorder-0Ij921El.js");
|
|
45
45
|
require("tls");
|
|
@@ -544,7 +544,11 @@ function formatRequestError(err, context) {
|
|
|
544
544
|
}
|
|
545
545
|
return lines.join("\n");
|
|
546
546
|
}
|
|
547
|
+
const streamControllers = /* @__PURE__ */ new Map();
|
|
547
548
|
function registerRequestHandler(ipc) {
|
|
549
|
+
handle.handleIpc(ipc, handle.IPC.request.stopStream, async (_e, streamId) => {
|
|
550
|
+
streamControllers.get(streamId)?.abort();
|
|
551
|
+
});
|
|
548
552
|
handle.handleIpc(ipc, handle.IPC.request.send, async (_e, payload) => {
|
|
549
553
|
ipcValidate.validateSendRequestPayload(payload);
|
|
550
554
|
const {
|
|
@@ -554,9 +558,23 @@ function registerRequestHandler(ipc) {
|
|
|
554
558
|
globals: payloadGlobals,
|
|
555
559
|
proxy,
|
|
556
560
|
tls,
|
|
557
|
-
piiMaskPatterns = []
|
|
561
|
+
piiMaskPatterns = [],
|
|
562
|
+
streamId,
|
|
563
|
+
forceStream
|
|
558
564
|
} = payload;
|
|
559
565
|
requestExec.applyRequestDefaults(req);
|
|
566
|
+
const maskEvent = (ev) => piiMaskPatterns.length ? { ...ev, data: requestExec.maskPii(ev.data, piiMaskPatterns), json: void 0 } : ev;
|
|
567
|
+
let abortController;
|
|
568
|
+
let flushTimer;
|
|
569
|
+
const pending = [];
|
|
570
|
+
const flushStream = () => {
|
|
571
|
+
if (pending.length) _e.sender.send(handle.IPC.request.streamEvent, { streamId, events: pending.splice(0) });
|
|
572
|
+
};
|
|
573
|
+
if (streamId) {
|
|
574
|
+
abortController = new AbortController();
|
|
575
|
+
streamControllers.set(streamId, abortController);
|
|
576
|
+
flushTimer = setInterval(flushStream, 40);
|
|
577
|
+
}
|
|
560
578
|
const start = Date.now();
|
|
561
579
|
const liveGlobals = requestExec.getGlobals();
|
|
562
580
|
const mergedGlobals = { ...payloadGlobals, ...liveGlobals };
|
|
@@ -640,18 +658,27 @@ function registerRequestHandler(ipc) {
|
|
|
640
658
|
tls,
|
|
641
659
|
onSent: (sent) => {
|
|
642
660
|
sentRequest = sent;
|
|
643
|
-
}
|
|
661
|
+
},
|
|
662
|
+
forceStream,
|
|
663
|
+
signal: abortController?.signal,
|
|
664
|
+
onStreamEvent: streamId ? (ev) => {
|
|
665
|
+
pending.push(maskEvent(ev));
|
|
666
|
+
if (pending.length >= 50) flushStream();
|
|
667
|
+
} : void 0
|
|
644
668
|
});
|
|
645
669
|
const maskedBody = requestExec.maskPii(exchange.responseBody, piiMaskPatterns);
|
|
646
670
|
const maskedHeaders = requestExec.maskHeaders(exchange.rawHeaders, piiMaskPatterns);
|
|
647
671
|
const bodySize = Buffer.byteLength(exchange.responseBody, "utf8");
|
|
672
|
+
const maskedEvents = exchange.events?.map(maskEvent);
|
|
673
|
+
const streamFields = exchange.streamed ? { streamed: true, events: maskedEvents, streamClose: exchange.streamClose, firstEventMs: exchange.firstEventMs } : {};
|
|
648
674
|
response = {
|
|
649
675
|
status: exchange.status,
|
|
650
676
|
statusText: exchange.statusText,
|
|
651
677
|
headers: maskedHeaders,
|
|
652
678
|
body: maskedBody,
|
|
653
679
|
bodySize,
|
|
654
|
-
durationMs: exchange.durationMs
|
|
680
|
+
durationMs: exchange.durationMs,
|
|
681
|
+
...streamFields
|
|
655
682
|
};
|
|
656
683
|
scriptResponse = {
|
|
657
684
|
status: exchange.status,
|
|
@@ -659,7 +686,8 @@ function registerRequestHandler(ipc) {
|
|
|
659
686
|
headers: exchange.rawHeaders,
|
|
660
687
|
body: exchange.responseBody,
|
|
661
688
|
bodySize,
|
|
662
|
-
durationMs: exchange.durationMs
|
|
689
|
+
durationMs: exchange.durationMs,
|
|
690
|
+
...exchange.streamed ? { streamed: true, events: exchange.events } : {}
|
|
663
691
|
};
|
|
664
692
|
} catch (err) {
|
|
665
693
|
const diagnostic = formatRequestError(err, {
|
|
@@ -680,6 +708,12 @@ function registerRequestHandler(ipc) {
|
|
|
680
708
|
error: diagnostic
|
|
681
709
|
};
|
|
682
710
|
scriptResponse = response;
|
|
711
|
+
} finally {
|
|
712
|
+
if (streamId) {
|
|
713
|
+
if (flushTimer) clearInterval(flushTimer);
|
|
714
|
+
flushStream();
|
|
715
|
+
streamControllers.delete(streamId);
|
|
716
|
+
}
|
|
683
717
|
}
|
|
684
718
|
const schemaTestResults = !response.error ? requestExec.buildSchemaTestResults(req.schema, scriptResponse.body) : [];
|
|
685
719
|
let postTestResults = [];
|
|
@@ -889,21 +923,71 @@ function translateMaybe(src, format) {
|
|
|
889
923
|
}
|
|
890
924
|
function parseRequest(item, collectionAuth) {
|
|
891
925
|
const req = typeof item.request === "string" ? { url: item.request, method: "GET" } : item.request ?? {};
|
|
926
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
927
|
+
const url = parseUrl(req.url);
|
|
928
|
+
const headers = parseHeaders$1(req.header);
|
|
929
|
+
const params = parseParams$1(req.url);
|
|
930
|
+
const body = parseBody$1(req.body);
|
|
931
|
+
const examples = parseExamples(item, { method, url, headers, params, body });
|
|
892
932
|
return {
|
|
893
933
|
id: uuid.v4(),
|
|
894
934
|
name: item.name ?? "Request",
|
|
895
|
-
method
|
|
896
|
-
url
|
|
897
|
-
headers
|
|
898
|
-
params
|
|
935
|
+
method,
|
|
936
|
+
url,
|
|
937
|
+
headers,
|
|
938
|
+
params,
|
|
899
939
|
auth: parseAuth$1(req.auth ?? collectionAuth),
|
|
900
|
-
body
|
|
940
|
+
body,
|
|
901
941
|
description: req.description ?? "",
|
|
902
942
|
preRequestScript: translateMaybe(parseScript(item.event, "prerequest"), "postman"),
|
|
903
943
|
postRequestScript: translateMaybe(parseScript(item.event, "test"), "postman"),
|
|
904
|
-
meta: {}
|
|
944
|
+
meta: {},
|
|
945
|
+
...examples ? { examples } : {}
|
|
905
946
|
};
|
|
906
947
|
}
|
|
948
|
+
function parseExamples(item, parent) {
|
|
949
|
+
const responses = item?.response;
|
|
950
|
+
if (!Array.isArray(responses) || responses.length === 0) return void 0;
|
|
951
|
+
const examples = [];
|
|
952
|
+
for (const r of responses) {
|
|
953
|
+
if (!r || typeof r !== "object") continue;
|
|
954
|
+
const orig = r.originalRequest;
|
|
955
|
+
const request = orig ? {
|
|
956
|
+
method: (orig.method ?? parent.method).toUpperCase(),
|
|
957
|
+
url: parseUrl(orig.url),
|
|
958
|
+
headers: parseHeaders$1(orig.header),
|
|
959
|
+
params: parseParams$1(orig.url),
|
|
960
|
+
body: parseBody$1(orig.body)
|
|
961
|
+
} : {
|
|
962
|
+
method: parent.method,
|
|
963
|
+
url: parent.url,
|
|
964
|
+
headers: parent.headers,
|
|
965
|
+
params: parent.params,
|
|
966
|
+
body: parent.body
|
|
967
|
+
};
|
|
968
|
+
const headers = {};
|
|
969
|
+
for (const h of Array.isArray(r.header) ? r.header : []) {
|
|
970
|
+
if (h && typeof h === "object" && h.key) headers[h.key] = h.value ?? "";
|
|
971
|
+
}
|
|
972
|
+
const body = typeof r.body === "string" ? r.body : "";
|
|
973
|
+
const code = typeof r.code === "number" ? r.code : 0;
|
|
974
|
+
examples.push({
|
|
975
|
+
id: uuid.v4(),
|
|
976
|
+
name: typeof r.name === "string" && r.name.trim() ? r.name : "Example",
|
|
977
|
+
request,
|
|
978
|
+
response: {
|
|
979
|
+
status: code,
|
|
980
|
+
statusText: typeof r.status === "string" ? r.status : "",
|
|
981
|
+
headers,
|
|
982
|
+
body,
|
|
983
|
+
bodySize: body.length,
|
|
984
|
+
durationMs: typeof r.responseTime === "number" ? r.responseTime : 0
|
|
985
|
+
},
|
|
986
|
+
source: "imported"
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
return examples.length ? examples : void 0;
|
|
990
|
+
}
|
|
907
991
|
function parseScript(events, listen) {
|
|
908
992
|
if (!Array.isArray(events)) return void 0;
|
|
909
993
|
const event = events.find((e) => e.listen === listen);
|
|
@@ -1063,6 +1147,45 @@ function buildBody(operation, spec) {
|
|
|
1063
1147
|
}
|
|
1064
1148
|
return { mode: "none" };
|
|
1065
1149
|
}
|
|
1150
|
+
function buildExamples(operation, spec, baseParams, baseHeaders, baseBody) {
|
|
1151
|
+
const json = resolve(spec, operation.requestBody?.content ?? {})["application/json"];
|
|
1152
|
+
const bodyExamples = {};
|
|
1153
|
+
if (json?.examples && typeof json.examples === "object") {
|
|
1154
|
+
for (const [name, ex] of Object.entries(json.examples)) {
|
|
1155
|
+
bodyExamples[name] = resolve(spec, ex)?.value;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
const paramExamples = {};
|
|
1159
|
+
for (const p of operation.parameters ?? []) {
|
|
1160
|
+
if (!p?.name || p.in !== "query" && p.in !== "path") continue;
|
|
1161
|
+
if (p.examples && typeof p.examples === "object") {
|
|
1162
|
+
paramExamples[p.name] = {};
|
|
1163
|
+
for (const [name, ex] of Object.entries(p.examples)) {
|
|
1164
|
+
paramExamples[p.name][name] = resolve(spec, ex)?.value;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
const names = /* @__PURE__ */ new Set([
|
|
1169
|
+
...Object.keys(bodyExamples),
|
|
1170
|
+
...Object.values(paramExamples).flatMap((m) => Object.keys(m))
|
|
1171
|
+
]);
|
|
1172
|
+
if (names.size === 0) return void 0;
|
|
1173
|
+
const examples = [];
|
|
1174
|
+
for (const name of names) {
|
|
1175
|
+
const body = name in bodyExamples && bodyExamples[name] !== void 0 ? { mode: "json", json: typeof bodyExamples[name] === "string" ? String(bodyExamples[name]) : JSON.stringify(bodyExamples[name], null, 2) } : baseBody;
|
|
1176
|
+
const params = baseParams.some((pr) => paramExamples[pr.key]?.[name] !== void 0) ? baseParams.map((pr) => {
|
|
1177
|
+
const v = paramExamples[pr.key]?.[name];
|
|
1178
|
+
return v !== void 0 ? { ...pr, value: v === null ? "" : String(v) } : pr;
|
|
1179
|
+
}) : baseParams;
|
|
1180
|
+
examples.push({
|
|
1181
|
+
id: uuid.v4(),
|
|
1182
|
+
name,
|
|
1183
|
+
request: { headers: baseHeaders, params, body },
|
|
1184
|
+
source: "imported"
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
return examples.length ? examples : void 0;
|
|
1188
|
+
}
|
|
1066
1189
|
function buildParams(operation) {
|
|
1067
1190
|
return (operation.parameters ?? []).filter((p) => p.in === "query").map((p) => ({
|
|
1068
1191
|
key: p.name,
|
|
@@ -1159,18 +1282,22 @@ function buildCollection(spec) {
|
|
|
1159
1282
|
];
|
|
1160
1283
|
const rawOperation = pathItem[method];
|
|
1161
1284
|
const responseSchema = buildResponseSchema(rawOperation, spec);
|
|
1285
|
+
const headers = buildHeaders(opWithParams);
|
|
1286
|
+
const body = buildBody(opWithParams, spec);
|
|
1287
|
+
const examples = buildExamples(opWithParams, spec, params, headers, body);
|
|
1162
1288
|
const req = {
|
|
1163
1289
|
id: uuid.v4(),
|
|
1164
1290
|
name: operation.summary ?? operation.operationId ?? `${method.toUpperCase()} ${pathStr}`,
|
|
1165
1291
|
method: method.toUpperCase(),
|
|
1166
1292
|
url: rewritePathTemplate(`${baseUrl}${pathStr}`),
|
|
1167
|
-
headers
|
|
1293
|
+
headers,
|
|
1168
1294
|
params,
|
|
1169
1295
|
auth: buildAuth(security, securitySchemes),
|
|
1170
|
-
body
|
|
1296
|
+
body,
|
|
1171
1297
|
description: operation.description ?? "",
|
|
1172
1298
|
meta: { tags },
|
|
1173
|
-
...responseSchema ? { schema: responseSchema } : {}
|
|
1299
|
+
...responseSchema ? { schema: responseSchema } : {},
|
|
1300
|
+
...examples ? { examples } : {}
|
|
1174
1301
|
};
|
|
1175
1302
|
requests[req.id] = req;
|
|
1176
1303
|
if (!foldersByTag[tag]) {
|
|
@@ -4899,8 +5026,24 @@ async function resolveSnapshotSpec(relPath) {
|
|
|
4899
5026
|
function registerContractHandlers(ipc) {
|
|
4900
5027
|
handle.handleIpc(ipc, handle.IPC.contract.run, async (_e, payload) => {
|
|
4901
5028
|
ipcValidate.validateContractRunPayload(payload);
|
|
4902
|
-
const { mode,
|
|
5029
|
+
const { mode, envVars, collectionVars = {}, requestBaseUrl, providerBaseUrl, stateHandlerUrl } = payload;
|
|
5030
|
+
let requests = payload.requests;
|
|
4903
5031
|
let { specUrl, specPath } = payload;
|
|
5032
|
+
if (mode !== "provider") {
|
|
5033
|
+
const designReqs = await snapshots.loadDesignContractRequests(
|
|
5034
|
+
{ designContracts: payload.designContracts },
|
|
5035
|
+
getWorkspaceDir() ?? void 0
|
|
5036
|
+
);
|
|
5037
|
+
const keyOf = (r) => `${r.method} ${r.url} ${r.name}`;
|
|
5038
|
+
const seen = new Set(requests.map(keyOf));
|
|
5039
|
+
const fresh = designReqs.filter((r) => !seen.has(keyOf(r)));
|
|
5040
|
+
if (fresh.length) {
|
|
5041
|
+
requests = [...requests, ...fresh];
|
|
5042
|
+
if (envVars["baseUrl"] === void 0 && collectionVars["baseUrl"] === void 0) {
|
|
5043
|
+
collectionVars["baseUrl"] = "";
|
|
5044
|
+
}
|
|
5045
|
+
}
|
|
5046
|
+
}
|
|
4904
5047
|
if (payload.specSnapshotRelPath) {
|
|
4905
5048
|
const resolved = await resolveSnapshotSpec(payload.specSnapshotRelPath);
|
|
4906
5049
|
specPath = resolved.specPath;
|
|
@@ -4931,6 +5074,16 @@ function registerContractHandlers(ipc) {
|
|
|
4931
5074
|
await promises.writeFile(filePath, snapshots.reportToHtml(report, { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), ...meta }), "utf8");
|
|
4932
5075
|
return true;
|
|
4933
5076
|
});
|
|
5077
|
+
handle.handleIpc(ipc, handle.IPC.contract.exportDesignPact, async (_e, contract) => {
|
|
5078
|
+
const dir = getWorkspaceDir();
|
|
5079
|
+
if (!dir) throw new Error("No workspace open — open or save a workspace first.");
|
|
5080
|
+
const safe = (s) => (s || "unnamed").replace(/[^a-zA-Z0-9._-]+/g, "-");
|
|
5081
|
+
const relPath = path.join("pacts", `${safe(contract.consumer)}-${safe(contract.provider)}.pact.json`);
|
|
5082
|
+
const fullPath = path.join(dir, relPath);
|
|
5083
|
+
await promises.mkdir(path.join(dir, "pacts"), { recursive: true });
|
|
5084
|
+
await promises.writeFile(fullPath, JSON.stringify(snapshots.designContractToPact(contract), null, 2), "utf8");
|
|
5085
|
+
return relPath;
|
|
5086
|
+
});
|
|
4934
5087
|
handle.handleIpc(ipc, handle.IPC.contract.captureSnapshot, async (_e, opts) => {
|
|
4935
5088
|
const dir = getWorkspaceDir();
|
|
4936
5089
|
if (!dir) throw new Error("No workspace open - cannot capture snapshot.");
|
|
@@ -5165,6 +5318,198 @@ function registerRecordHandlers(ipc, getWebContents) {
|
|
|
5165
5318
|
return recorder.entriesToMockServer(entries, upstream, name, port);
|
|
5166
5319
|
});
|
|
5167
5320
|
}
|
|
5321
|
+
const CLOUD_TOKEN_REF = "cloud:token";
|
|
5322
|
+
const CLOUD_ENDPOINT = (process.env["API_SPECTOR_CLOUD_ENDPOINT"] || "https://api-spector.dev").replace(/\/+$/, "");
|
|
5323
|
+
async function cloudFetch(path2, method, body) {
|
|
5324
|
+
const token = await requestExec.getSecret(CLOUD_TOKEN_REF);
|
|
5325
|
+
if (!token) throw new Error("No cloud API token set. Add one in Settings → Cloud.");
|
|
5326
|
+
const dispatcher = await requestExec.buildDispatcher();
|
|
5327
|
+
const url = CLOUD_ENDPOINT + path2;
|
|
5328
|
+
let res;
|
|
5329
|
+
try {
|
|
5330
|
+
res = await undici.fetch(url, {
|
|
5331
|
+
method,
|
|
5332
|
+
headers: {
|
|
5333
|
+
"Content-Type": "application/json",
|
|
5334
|
+
Accept: "application/json",
|
|
5335
|
+
Authorization: `Bearer ${token}`
|
|
5336
|
+
},
|
|
5337
|
+
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
5338
|
+
dispatcher
|
|
5339
|
+
});
|
|
5340
|
+
} catch (err) {
|
|
5341
|
+
const msg = err.message;
|
|
5342
|
+
const hint = /ECONNREFUSED|fetch failed|connect|ENOTFOUND/i.test(msg) ? ` — is API Spector Cloud running and reachable at ${CLOUD_ENDPOINT}? (set API_SPECTOR_CLOUD_ENDPOINT or start the broker)` : "";
|
|
5343
|
+
throw new Error(`Could not reach ${url}: ${msg}${hint}`);
|
|
5344
|
+
}
|
|
5345
|
+
const text = await res.text();
|
|
5346
|
+
let json;
|
|
5347
|
+
try {
|
|
5348
|
+
json = text ? JSON.parse(text) : {};
|
|
5349
|
+
} catch {
|
|
5350
|
+
json = { raw: text };
|
|
5351
|
+
}
|
|
5352
|
+
if (!res.ok) {
|
|
5353
|
+
let detail = json?.error || json?.message || (res.status === 401 ? "Unauthorized (check your token)" : `HTTP ${res.status}`);
|
|
5354
|
+
if (res.status === 403 && json?.limit != null) detail += ` (plan limit: ${json.limit})`;
|
|
5355
|
+
if (res.status === 405) detail = `HTTP 405 from ${CLOUD_ENDPOINT} — this does not look like the API Spector Cloud API (set API_SPECTOR_CLOUD_ENDPOINT to your broker URL).`;
|
|
5356
|
+
throw new Error(detail);
|
|
5357
|
+
}
|
|
5358
|
+
return json;
|
|
5359
|
+
}
|
|
5360
|
+
function interpolateStatic(str, vars) {
|
|
5361
|
+
return str.replace(/\{\{([^}]+)\}\}/g, (m, key) => {
|
|
5362
|
+
const trimmed = String(key).trim();
|
|
5363
|
+
return Object.prototype.hasOwnProperty.call(vars, trimmed) ? vars[trimmed] : m;
|
|
5364
|
+
});
|
|
5365
|
+
}
|
|
5366
|
+
function resolveRequest(req, vars) {
|
|
5367
|
+
return {
|
|
5368
|
+
id: req.id,
|
|
5369
|
+
name: req.name,
|
|
5370
|
+
method: req.method,
|
|
5371
|
+
url: requestExec.buildUrl(req.url, req.params ?? [], vars),
|
|
5372
|
+
headers: resolveHeaders(req.headers, vars),
|
|
5373
|
+
params: [],
|
|
5374
|
+
// folded into url; empty so the runner doesn't re-append
|
|
5375
|
+
auth: req.auth ?? { type: "none" },
|
|
5376
|
+
body: resolveBody(req.body, vars),
|
|
5377
|
+
preRequestScript: req.preRequestScript || void 0,
|
|
5378
|
+
postRequestScript: req.postRequestScript || void 0
|
|
5379
|
+
};
|
|
5380
|
+
}
|
|
5381
|
+
function resolveHeaders(headers, vars) {
|
|
5382
|
+
return (headers ?? []).filter((h) => h.enabled !== false && h.key).map((h) => ({ key: interpolateStatic(h.key, vars), value: interpolateStatic(h.value ?? "", vars), enabled: true }));
|
|
5383
|
+
}
|
|
5384
|
+
function resolveBody(body, vars) {
|
|
5385
|
+
if (!body || !body.mode || body.mode === "none") return { mode: "none" };
|
|
5386
|
+
const b = { ...body };
|
|
5387
|
+
if (body.mode === "json" && body.json != null) b.json = interpolateStatic(body.json, vars);
|
|
5388
|
+
else if (body.mode === "raw" && body.raw != null) b.raw = interpolateStatic(body.raw, vars);
|
|
5389
|
+
else if (body.mode === "form" && body.form) {
|
|
5390
|
+
b.form = body.form.map((f) => ({ ...f, key: interpolateStatic(f.key, vars), value: interpolateStatic(f.value ?? "", vars) }));
|
|
5391
|
+
} else if (body.mode === "graphql" && body.graphql) {
|
|
5392
|
+
b.graphql = {
|
|
5393
|
+
...body.graphql,
|
|
5394
|
+
query: interpolateStatic(body.graphql.query ?? "", vars),
|
|
5395
|
+
variables: interpolateStatic(body.graphql.variables ?? "", vars)
|
|
5396
|
+
};
|
|
5397
|
+
} else if (body.mode === "soap" && body.soap) {
|
|
5398
|
+
b.soap = { ...body.soap, envelope: interpolateStatic(body.soap.envelope ?? "", vars) };
|
|
5399
|
+
}
|
|
5400
|
+
return b;
|
|
5401
|
+
}
|
|
5402
|
+
function setVarNames(reqs) {
|
|
5403
|
+
const names = /* @__PURE__ */ new Set();
|
|
5404
|
+
const re = /sp\.(?:environment|collectionVariables|variables|globals)\.set\(\s*['"`]([^'"`]+)['"`]/g;
|
|
5405
|
+
for (const r of reqs) {
|
|
5406
|
+
for (const script of [r.preRequestScript, r.postRequestScript]) {
|
|
5407
|
+
if (!script) continue;
|
|
5408
|
+
let m;
|
|
5409
|
+
while ((m = re.exec(script)) !== null) names.add(m[1]);
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5412
|
+
return names;
|
|
5413
|
+
}
|
|
5414
|
+
function omitKeys(vars, names) {
|
|
5415
|
+
if (!names.size) return vars;
|
|
5416
|
+
const out = {};
|
|
5417
|
+
for (const [k, v] of Object.entries(vars)) if (!names.has(k)) out[k] = v;
|
|
5418
|
+
return out;
|
|
5419
|
+
}
|
|
5420
|
+
function slugify(name) {
|
|
5421
|
+
return (name || "mock").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 63) || "mock";
|
|
5422
|
+
}
|
|
5423
|
+
function registerCloudHandlers(ipc) {
|
|
5424
|
+
handle.handleIpc(ipc, handle.IPC.cloud.test, async () => {
|
|
5425
|
+
return cloudFetch("/api/me", "GET");
|
|
5426
|
+
});
|
|
5427
|
+
handle.handleIpc(ipc, handle.IPC.cloud.pushMock, async (_e, server) => {
|
|
5428
|
+
const payload = {
|
|
5429
|
+
name: server.name,
|
|
5430
|
+
slug: slugify(server.name),
|
|
5431
|
+
enabled: true,
|
|
5432
|
+
auth_type: "none",
|
|
5433
|
+
routes: (server.routes ?? []).map((r) => ({
|
|
5434
|
+
method: r.method,
|
|
5435
|
+
path: r.path,
|
|
5436
|
+
status: r.statusCode,
|
|
5437
|
+
headers: r.headers ?? {},
|
|
5438
|
+
body: r.body ?? "",
|
|
5439
|
+
delay: r.delay ?? 0,
|
|
5440
|
+
script: r.script ?? null
|
|
5441
|
+
}))
|
|
5442
|
+
};
|
|
5443
|
+
return cloudFetch("/api/mocks", "POST", payload);
|
|
5444
|
+
});
|
|
5445
|
+
handle.handleIpc(ipc, handle.IPC.cloud.getMock, async (_e, name) => {
|
|
5446
|
+
return cloudFetch("/api/mocks/" + encodeURIComponent(slugify(name)), "GET");
|
|
5447
|
+
});
|
|
5448
|
+
handle.handleIpc(ipc, handle.IPC.cloud.pushPact, async (_e, input) => {
|
|
5449
|
+
const pact = snapshots.exportPact(input.consumer, input.provider, input.requests ?? []);
|
|
5450
|
+
return cloudFetch("/api/contracts", "PUT", {
|
|
5451
|
+
consumer: input.consumer,
|
|
5452
|
+
consumerVersion: input.consumerVersion,
|
|
5453
|
+
provider: input.provider,
|
|
5454
|
+
content: pact
|
|
5455
|
+
});
|
|
5456
|
+
});
|
|
5457
|
+
handle.handleIpc(ipc, handle.IPC.cloud.pushDesignContract, async (_e, input) => {
|
|
5458
|
+
const pact = snapshots.designContractToPact(input.contract);
|
|
5459
|
+
return cloudFetch("/api/contracts", "PUT", {
|
|
5460
|
+
consumer: input.contract.consumer,
|
|
5461
|
+
consumerVersion: input.consumerVersion,
|
|
5462
|
+
provider: input.contract.provider,
|
|
5463
|
+
content: pact
|
|
5464
|
+
});
|
|
5465
|
+
});
|
|
5466
|
+
handle.handleIpc(ipc, handle.IPC.cloud.pushSpec, async (_e, input) => {
|
|
5467
|
+
let specText = input.spec;
|
|
5468
|
+
if (!specText && input.specUrl) {
|
|
5469
|
+
const res = await undici.fetch(input.specUrl, { dispatcher: await requestExec.buildDispatcher() });
|
|
5470
|
+
if (!res.ok) throw new Error(`Could not fetch spec from ${input.specUrl} (HTTP ${res.status})`);
|
|
5471
|
+
specText = await res.text();
|
|
5472
|
+
}
|
|
5473
|
+
if (!specText) throw new Error("Provide an OpenAPI spec (text or a spec URL).");
|
|
5474
|
+
let spec;
|
|
5475
|
+
try {
|
|
5476
|
+
spec = JSON.parse(specText);
|
|
5477
|
+
} catch {
|
|
5478
|
+
spec = jsYaml.load(specText);
|
|
5479
|
+
}
|
|
5480
|
+
if (!spec || typeof spec !== "object") {
|
|
5481
|
+
throw new Error("Could not parse the OpenAPI spec (expected JSON or YAML).");
|
|
5482
|
+
}
|
|
5483
|
+
return cloudFetch("/api/provider-contracts", "PUT", {
|
|
5484
|
+
pacticipant: input.pacticipant,
|
|
5485
|
+
version: input.version,
|
|
5486
|
+
spec
|
|
5487
|
+
});
|
|
5488
|
+
});
|
|
5489
|
+
handle.handleIpc(ipc, handle.IPC.cloud.openMatrix, async () => {
|
|
5490
|
+
await electron.shell.openExternal(CLOUD_ENDPOINT + "/matrix");
|
|
5491
|
+
});
|
|
5492
|
+
handle.handleIpc(ipc, handle.IPC.cloud.pushMonitor, async (_e, input) => {
|
|
5493
|
+
const { request, environment, collectionVars, globals } = input;
|
|
5494
|
+
const envVars = await requestExec.buildEnvVars(environment);
|
|
5495
|
+
const liveGlobals = { ...globals, ...requestExec.getGlobals() };
|
|
5496
|
+
const staticVars = requestExec.mergeVars(envVars, collectionVars ?? {}, liveGlobals, {}, {});
|
|
5497
|
+
const setup = input.setup ?? [];
|
|
5498
|
+
const resolveVars = omitKeys(staticVars, setVarNames(setup));
|
|
5499
|
+
const monitorRequest = resolveRequest(request, resolveVars);
|
|
5500
|
+
const setupRequests = setup.map((h) => resolveRequest(h, resolveVars));
|
|
5501
|
+
const payload = {
|
|
5502
|
+
name: input.name || request.name || "Monitor",
|
|
5503
|
+
target_url: monitorRequest.url,
|
|
5504
|
+
method: request.method,
|
|
5505
|
+
expected_status: input.expectedStatus ?? 200,
|
|
5506
|
+
interval_seconds: input.intervalSeconds ?? 300,
|
|
5507
|
+
request: monitorRequest,
|
|
5508
|
+
setup: setupRequests
|
|
5509
|
+
};
|
|
5510
|
+
return cloudFetch("/api/monitors", "POST", payload);
|
|
5511
|
+
});
|
|
5512
|
+
}
|
|
5168
5513
|
const REGISTRY_URL = "https://registry.npmjs.org/@testsmith/api-spector/latest";
|
|
5169
5514
|
function isNewer(a, b) {
|
|
5170
5515
|
const core = (v) => v.split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
|
|
@@ -5175,7 +5520,7 @@ function isNewer(a, b) {
|
|
|
5175
5520
|
return a2 > b2;
|
|
5176
5521
|
}
|
|
5177
5522
|
async function checkForUpdate() {
|
|
5178
|
-
const current = "0.4.
|
|
5523
|
+
const current = "0.4.8";
|
|
5179
5524
|
try {
|
|
5180
5525
|
const res = await fetch(REGISTRY_URL, { signal: AbortSignal.timeout(4e3) });
|
|
5181
5526
|
if (!res.ok) return null;
|
|
@@ -5295,6 +5640,7 @@ electron.app.whenReady().then(async () => {
|
|
|
5295
5640
|
registerContractHandlers(electron.ipcMain);
|
|
5296
5641
|
registerGitHandlers(electron.ipcMain);
|
|
5297
5642
|
registerRecordHandlers(electron.ipcMain, () => electron.BrowserWindow.getAllWindows()[0]?.webContents ?? null);
|
|
5643
|
+
registerCloudHandlers(electron.ipcMain);
|
|
5298
5644
|
handle.handleIpc(electron.ipcMain, handle.IPC.shell.openExternal, (_e, url) => electron.shell.openExternal(url));
|
|
5299
5645
|
handle.handleIpc(electron.ipcMain, handle.IPC.app.checkUpdate, () => checkForUpdate());
|
|
5300
5646
|
createWindow();
|
package/out/main/lib.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const mockServer = require("./chunks/mock-server-BxiXhP1m.js");
|
|
4
|
-
const requestExec = require("./chunks/request-exec-
|
|
4
|
+
const requestExec = require("./chunks/request-exec-BH-M3KqZ.js");
|
|
5
5
|
require("http");
|
|
6
6
|
require("crypto");
|
|
7
7
|
require("vm");
|
|
@@ -10,7 +10,7 @@ require("@xmldom/xmldom");
|
|
|
10
10
|
require("undici");
|
|
11
11
|
require("fs/promises");
|
|
12
12
|
require("tls");
|
|
13
|
-
require("./chunks/handle-
|
|
13
|
+
require("./chunks/handle-rimXXdJH.js");
|
|
14
14
|
require("path");
|
|
15
15
|
require("tv4");
|
|
16
16
|
require("jsonpath-plus");
|
package/out/main/mock.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
const mockServer = require("./chunks/mock-server-BxiXhP1m.js");
|
|
4
|
-
const cliCommon = require("./chunks/cli-common-
|
|
4
|
+
const cliCommon = require("./chunks/cli-common-BKYgsbGB.js");
|
|
5
5
|
require("http");
|
|
6
6
|
require("crypto");
|
|
7
7
|
require("vm");
|
package/out/main/record.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const promises = require("fs/promises");
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const recorder = require("./chunks/recorder-0Ij921El.js");
|
|
6
|
-
const cliCommon = require("./chunks/cli-common-
|
|
6
|
+
const cliCommon = require("./chunks/cli-common-BKYgsbGB.js");
|
|
7
7
|
require("http");
|
|
8
8
|
require("crypto");
|
|
9
9
|
require("undici");
|
package/out/main/runner.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
const promises = require("fs/promises");
|
|
4
4
|
const path = require("path");
|
|
5
|
-
const requestExec = require("./chunks/request-exec-
|
|
5
|
+
const requestExec = require("./chunks/request-exec-BH-M3KqZ.js");
|
|
6
6
|
const requestCollection = require("./chunks/request-collection-CJoXpvOw.js");
|
|
7
7
|
const environments = require("./chunks/environments-iM3SUM-4.js");
|
|
8
|
-
const cliCommon = require("./chunks/cli-common-
|
|
8
|
+
const cliCommon = require("./chunks/cli-common-BKYgsbGB.js");
|
|
9
9
|
require("undici");
|
|
10
10
|
require("tls");
|
|
11
|
-
require("./chunks/handle-
|
|
11
|
+
require("./chunks/handle-rimXXdJH.js");
|
|
12
12
|
require("crypto");
|
|
13
13
|
require("dayjs");
|
|
14
14
|
require("vm");
|
|
@@ -397,7 +397,7 @@ async function main() {
|
|
|
397
397
|
} else if (!envName && workspace.settings?.defaultEnvironment && !env) {
|
|
398
398
|
console.warn(cliCommon.color(`Warning: default environment "${workspace.settings.defaultEnvironment}" not found. Running without environment.`, cliCommon.C.yellow));
|
|
399
399
|
}
|
|
400
|
-
const version = `v${"0.4.
|
|
400
|
+
const version = `v${"0.4.8"}`;
|
|
401
401
|
console.log("");
|
|
402
402
|
console.log(cliCommon.color(" API Test Runner" + (version ? ` ${version}` : ""), cliCommon.C.bold, cliCommon.C.white));
|
|
403
403
|
console.log(cliCommon.color(` Workspace: ${wsPath}`, cliCommon.C.gray));
|
package/out/main/wsdl.js
CHANGED
|
@@ -4,12 +4,13 @@ const promises = require("fs/promises");
|
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const https = require("https");
|
|
6
6
|
const http = require("http");
|
|
7
|
-
const soapHandler = require("./chunks/soap-handler-
|
|
8
|
-
const _import = require("./chunks/import-
|
|
9
|
-
const cliCommon = require("./chunks/cli-common-
|
|
10
|
-
require("./chunks/handle-
|
|
7
|
+
const soapHandler = require("./chunks/soap-handler-BXx842MN.js");
|
|
8
|
+
const _import = require("./chunks/import-DcenB5Q_.js");
|
|
9
|
+
const cliCommon = require("./chunks/cli-common-BKYgsbGB.js");
|
|
10
|
+
require("./chunks/handle-rimXXdJH.js");
|
|
11
11
|
require("@xmldom/xmldom");
|
|
12
12
|
require("uuid");
|
|
13
|
+
require("crypto");
|
|
13
14
|
function fetchUrl(url) {
|
|
14
15
|
return new Promise((resolveP, rejectP) => {
|
|
15
16
|
const lib = url.startsWith("https") ? https : http;
|