@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/agents.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
const promises = require("fs/promises");
|
|
4
4
|
const path = require("path");
|
|
5
|
-
const cliCommon = require("./chunks/cli-common-
|
|
5
|
+
const cliCommon = require("./chunks/cli-common-BKYgsbGB.js");
|
|
6
|
+
require("crypto");
|
|
6
7
|
const AGENTS = {
|
|
7
8
|
claude: {
|
|
8
9
|
name: "Claude Code",
|
|
@@ -25,7 +25,11 @@ const IPC = {
|
|
|
25
25
|
},
|
|
26
26
|
// ─── HTTP execution ────────────────────────────────────────────────────────
|
|
27
27
|
request: {
|
|
28
|
-
send: "request:send"
|
|
28
|
+
send: "request:send",
|
|
29
|
+
/** Event (main→renderer): a batch of streamed response frames. */
|
|
30
|
+
streamEvent: "request:stream-event",
|
|
31
|
+
/** Renderer→main: abort an in-flight streamed read by streamId. */
|
|
32
|
+
stopStream: "request:stop-stream"
|
|
29
33
|
},
|
|
30
34
|
// ─── Secrets ───────────────────────────────────────────────────────────────
|
|
31
35
|
secret: {
|
|
@@ -105,6 +109,8 @@ const IPC = {
|
|
|
105
109
|
run: "contract:run",
|
|
106
110
|
inferSchema: "contract:inferSchema",
|
|
107
111
|
exportReportHtml: "contract:exportReportHtml",
|
|
112
|
+
/** Compile a design-first contract to a Pact file on disk (save dialog). */
|
|
113
|
+
exportDesignPact: "contract:exportDesignPact",
|
|
108
114
|
captureSnapshot: "contract:captureSnapshot",
|
|
109
115
|
listSnapshots: "contract:listSnapshots",
|
|
110
116
|
loadSnapshot: "contract:loadSnapshot",
|
|
@@ -159,6 +165,25 @@ const IPC = {
|
|
|
159
165
|
// ─── App ───────────────────────────────────────────────────────────────────
|
|
160
166
|
app: {
|
|
161
167
|
checkUpdate: "app:checkUpdate"
|
|
168
|
+
},
|
|
169
|
+
// ─── Cloud (API Spector Cloud integration) ──────────────────────────────────
|
|
170
|
+
cloud: {
|
|
171
|
+
/** Verify the endpoint + token (GET /api/me). */
|
|
172
|
+
test: "cloud:test",
|
|
173
|
+
/** Push a mock server definition (POST /api/mocks). */
|
|
174
|
+
pushMock: "cloud:pushMock",
|
|
175
|
+
/** Look up an existing cloud mock's routes (GET /api/mocks/{slug}). */
|
|
176
|
+
getMock: "cloud:getMock",
|
|
177
|
+
/** Push a request as a monitor, URL resolved (POST /api/monitors). */
|
|
178
|
+
pushMonitor: "cloud:pushMonitor",
|
|
179
|
+
/** Publish a consumer pact built from requests (PUT /api/contracts). */
|
|
180
|
+
pushPact: "cloud:pushPact",
|
|
181
|
+
/** Publish a design-first consumer contract as a pact (PUT /api/contracts). */
|
|
182
|
+
pushDesignContract: "cloud:pushDesignContract",
|
|
183
|
+
/** Publish a provider OpenAPI spec (PUT /api/provider-contracts). */
|
|
184
|
+
pushSpec: "cloud:pushSpec",
|
|
185
|
+
/** Open the cloud deployment matrix in the browser. */
|
|
186
|
+
openMatrix: "cloud:openMatrix"
|
|
162
187
|
}
|
|
163
188
|
};
|
|
164
189
|
function handleIpc(ipc, channel, fn) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const uuid = require("uuid");
|
|
4
|
-
const soapHandler = require("./soap-handler-
|
|
5
|
-
require("./handle-
|
|
4
|
+
const soapHandler = require("./soap-handler-BXx842MN.js");
|
|
5
|
+
require("./handle-rimXXdJH.js");
|
|
6
6
|
require("https");
|
|
7
7
|
require("http");
|
|
8
8
|
require("@xmldom/xmldom");
|
|
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
const undici = require("undici");
|
|
25
25
|
const promises = require("fs/promises");
|
|
26
26
|
const nodeTls = require("tls");
|
|
27
|
-
const handle = require("./handle-
|
|
27
|
+
const handle = require("./handle-rimXXdJH.js");
|
|
28
28
|
const crypto = require("crypto");
|
|
29
29
|
const path = require("path");
|
|
30
30
|
const dayjs = require("dayjs");
|
|
@@ -637,6 +637,178 @@ function normalizeProxyInput(input) {
|
|
|
637
637
|
function ensureScheme(value) {
|
|
638
638
|
return /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//.test(value) ? value : `http://${value}`;
|
|
639
639
|
}
|
|
640
|
+
function detectStreamKind(contentType, forceStream = false) {
|
|
641
|
+
const ct = (contentType || "").toLowerCase();
|
|
642
|
+
if (ct.includes("text/event-stream")) return "sse";
|
|
643
|
+
if (ct.includes("application/x-ndjson") || ct.includes("application/ndjson") || ct.includes("application/jsonl") || ct.includes("application/x-jsonlines") || ct.includes("application/stream+json") || ct.includes("application/json-seq")) return "ndjson";
|
|
644
|
+
return forceStream ? "chunk" : null;
|
|
645
|
+
}
|
|
646
|
+
class StreamFramer {
|
|
647
|
+
constructor(mode) {
|
|
648
|
+
this.mode = mode;
|
|
649
|
+
}
|
|
650
|
+
carry = "";
|
|
651
|
+
/** Feed a decoded text chunk; returns the frames that completed. */
|
|
652
|
+
push(text) {
|
|
653
|
+
this.carry += text.replace(/\r\n?/g, "\n");
|
|
654
|
+
return this.mode === "sse" ? this.drainSse() : this.drainNdjson();
|
|
655
|
+
}
|
|
656
|
+
/** Emit whatever remains once the stream closes (a last line/block with no
|
|
657
|
+
* trailing delimiter). */
|
|
658
|
+
flush() {
|
|
659
|
+
const rest = this.carry;
|
|
660
|
+
this.carry = "";
|
|
661
|
+
if (this.mode === "ndjson") {
|
|
662
|
+
const f2 = this.ndjsonLine(rest);
|
|
663
|
+
return f2 ? [f2] : [];
|
|
664
|
+
}
|
|
665
|
+
const f = this.parseSseBlock(rest);
|
|
666
|
+
return f ? [f] : [];
|
|
667
|
+
}
|
|
668
|
+
drainSse() {
|
|
669
|
+
const out = [];
|
|
670
|
+
let idx;
|
|
671
|
+
while ((idx = this.carry.indexOf("\n\n")) !== -1) {
|
|
672
|
+
const block = this.carry.slice(0, idx);
|
|
673
|
+
this.carry = this.carry.slice(idx + 2);
|
|
674
|
+
const f = this.parseSseBlock(block);
|
|
675
|
+
if (f) out.push(f);
|
|
676
|
+
}
|
|
677
|
+
return out;
|
|
678
|
+
}
|
|
679
|
+
parseSseBlock(block) {
|
|
680
|
+
let name;
|
|
681
|
+
let id;
|
|
682
|
+
const dataLines = [];
|
|
683
|
+
for (const line of block.split("\n")) {
|
|
684
|
+
if (line === "" || line.startsWith(":")) continue;
|
|
685
|
+
const colon = line.indexOf(":");
|
|
686
|
+
const field = colon === -1 ? line : line.slice(0, colon);
|
|
687
|
+
let value = colon === -1 ? "" : line.slice(colon + 1);
|
|
688
|
+
if (value.startsWith(" ")) value = value.slice(1);
|
|
689
|
+
if (field === "data") dataLines.push(value);
|
|
690
|
+
else if (field === "event") name = value;
|
|
691
|
+
else if (field === "id") id = value;
|
|
692
|
+
}
|
|
693
|
+
if (dataLines.length === 0) return null;
|
|
694
|
+
return { name: name ?? "message", id, data: dataLines.join("\n") };
|
|
695
|
+
}
|
|
696
|
+
drainNdjson() {
|
|
697
|
+
const out = [];
|
|
698
|
+
let idx;
|
|
699
|
+
while ((idx = this.carry.indexOf("\n")) !== -1) {
|
|
700
|
+
const line = this.carry.slice(0, idx);
|
|
701
|
+
this.carry = this.carry.slice(idx + 1);
|
|
702
|
+
const f = this.ndjsonLine(line);
|
|
703
|
+
if (f) out.push(f);
|
|
704
|
+
}
|
|
705
|
+
return out;
|
|
706
|
+
}
|
|
707
|
+
ndjsonLine(line) {
|
|
708
|
+
const body = line.charCodeAt(0) === 30 ? line.slice(1) : line;
|
|
709
|
+
const s = body.trim();
|
|
710
|
+
return s ? { data: s } : null;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
function toEvent(frame, kind, seq, tMs) {
|
|
714
|
+
let json;
|
|
715
|
+
const trimmed = frame.data.trim();
|
|
716
|
+
if (trimmed && (trimmed[0] === "{" || trimmed[0] === "[")) {
|
|
717
|
+
try {
|
|
718
|
+
json = JSON.parse(trimmed);
|
|
719
|
+
} catch {
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
const ev = { seq, tMs, kind, data: frame.data };
|
|
723
|
+
if (frame.name !== void 0) ev.name = frame.name;
|
|
724
|
+
if (frame.id !== void 0) ev.id = frame.id;
|
|
725
|
+
if (json !== void 0) ev.json = json;
|
|
726
|
+
return ev;
|
|
727
|
+
}
|
|
728
|
+
async function readStream(body, kind, startMs, opts = {}) {
|
|
729
|
+
const now = opts.now ?? Date.now;
|
|
730
|
+
const maxEvents = opts.maxEvents ?? 5e3;
|
|
731
|
+
const maxMs = opts.maxMs ?? 3e5;
|
|
732
|
+
const idleMs = opts.idleMs ?? 6e4;
|
|
733
|
+
let lastActivity = now();
|
|
734
|
+
const framer = kind === "chunk" ? null : new StreamFramer(kind === "sse" ? "sse" : "ndjson");
|
|
735
|
+
const decoder = new TextDecoder("utf-8", { fatal: false });
|
|
736
|
+
const events = [];
|
|
737
|
+
let text = "";
|
|
738
|
+
let firstEventMs = -1;
|
|
739
|
+
let close = "complete";
|
|
740
|
+
if (!body) return { events, text, close, firstEventMs: 0 };
|
|
741
|
+
const emit = (frames) => {
|
|
742
|
+
for (const frame of frames) {
|
|
743
|
+
const tMs = now() - startMs;
|
|
744
|
+
const ev = toEvent(frame, kind, events.length, tMs);
|
|
745
|
+
if (firstEventMs < 0) firstEventMs = tMs;
|
|
746
|
+
events.push(ev);
|
|
747
|
+
opts.onEvent?.(ev);
|
|
748
|
+
if (events.length >= maxEvents) return false;
|
|
749
|
+
}
|
|
750
|
+
return true;
|
|
751
|
+
};
|
|
752
|
+
const reader = body.getReader();
|
|
753
|
+
try {
|
|
754
|
+
for (; ; ) {
|
|
755
|
+
if (opts.signal?.aborted) {
|
|
756
|
+
close = "stopped";
|
|
757
|
+
break;
|
|
758
|
+
}
|
|
759
|
+
const totalRem = maxMs > 0 ? maxMs - (now() - startMs) : Infinity;
|
|
760
|
+
const idleRem = idleMs > 0 ? idleMs - (now() - lastActivity) : Infinity;
|
|
761
|
+
if (totalRem <= 0 || idleRem <= 0) {
|
|
762
|
+
close = "timeout";
|
|
763
|
+
break;
|
|
764
|
+
}
|
|
765
|
+
const wait = Math.min(totalRem, idleRem);
|
|
766
|
+
let step;
|
|
767
|
+
if (wait === Infinity) {
|
|
768
|
+
step = await reader.read();
|
|
769
|
+
} else {
|
|
770
|
+
let timer;
|
|
771
|
+
const timeout = new Promise((res) => {
|
|
772
|
+
timer = setTimeout(() => res("timeout"), wait);
|
|
773
|
+
});
|
|
774
|
+
step = await Promise.race([reader.read(), timeout]);
|
|
775
|
+
if (timer) clearTimeout(timer);
|
|
776
|
+
}
|
|
777
|
+
if (step === "timeout") {
|
|
778
|
+
close = "timeout";
|
|
779
|
+
break;
|
|
780
|
+
}
|
|
781
|
+
const { value, done } = step;
|
|
782
|
+
if (done) break;
|
|
783
|
+
if (!value) continue;
|
|
784
|
+
lastActivity = now();
|
|
785
|
+
const chunkText = decoder.decode(value, { stream: true });
|
|
786
|
+
text += chunkText;
|
|
787
|
+
const keepGoing = framer ? emit(framer.push(chunkText)) : emit([{ data: chunkText }]);
|
|
788
|
+
if (!keepGoing) {
|
|
789
|
+
close = "stopped";
|
|
790
|
+
break;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
if (close === "complete") {
|
|
794
|
+
const tail = decoder.decode();
|
|
795
|
+
if (tail) {
|
|
796
|
+
text += tail;
|
|
797
|
+
if (framer) emit(framer.push(tail));
|
|
798
|
+
else emit([{ data: tail }]);
|
|
799
|
+
}
|
|
800
|
+
if (framer) emit(framer.flush());
|
|
801
|
+
}
|
|
802
|
+
} catch {
|
|
803
|
+
close = "error";
|
|
804
|
+
} finally {
|
|
805
|
+
try {
|
|
806
|
+
await reader.cancel();
|
|
807
|
+
} catch {
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
return { events, text, close, firstEventMs: firstEventMs < 0 ? 0 : firstEventMs };
|
|
811
|
+
}
|
|
640
812
|
const SIGNATURE = Buffer.from("NTLMSSP\0", "latin1");
|
|
641
813
|
const F_UNICODE = 1;
|
|
642
814
|
const F_OEM = 2;
|
|
@@ -1181,6 +1353,11 @@ function applyRequestDefaults(req) {
|
|
|
1181
1353
|
if (!req.body) req.body = { mode: "none" };
|
|
1182
1354
|
if (!req.auth) req.auth = { type: "none" };
|
|
1183
1355
|
}
|
|
1356
|
+
let _defaultAgent;
|
|
1357
|
+
function defaultAgent() {
|
|
1358
|
+
if (!_defaultAgent) _defaultAgent = new undici.Agent({ allowH2: true });
|
|
1359
|
+
return _defaultAgent;
|
|
1360
|
+
}
|
|
1184
1361
|
async function buildDispatcher(proxy, tls) {
|
|
1185
1362
|
if (proxy?.url?.trim()) trustSystemCertificateStore();
|
|
1186
1363
|
const connectOpts = {};
|
|
@@ -1213,13 +1390,14 @@ async function buildDispatcher(proxy, tls) {
|
|
|
1213
1390
|
return new undici.ProxyAgent({
|
|
1214
1391
|
uri: buildProxyUri({ url: proxy.url, auth: proxy.auth }),
|
|
1215
1392
|
requestTls: hasTls ? connectOpts : void 0,
|
|
1216
|
-
proxyTls: hasTls ? connectOpts : void 0
|
|
1393
|
+
proxyTls: hasTls ? connectOpts : void 0,
|
|
1394
|
+
allowH2: true
|
|
1217
1395
|
});
|
|
1218
1396
|
}
|
|
1219
1397
|
if (hasTls) {
|
|
1220
|
-
return new undici.Agent({ connect: connectOpts });
|
|
1398
|
+
return new undici.Agent({ connect: connectOpts, allowH2: true });
|
|
1221
1399
|
}
|
|
1222
|
-
return
|
|
1400
|
+
return defaultAgent();
|
|
1223
1401
|
}
|
|
1224
1402
|
function buildBodyAndApplyHeaders(req, vars, headers) {
|
|
1225
1403
|
let body;
|
|
@@ -1323,11 +1501,27 @@ async function performHttpExchange(opts) {
|
|
|
1323
1501
|
sentHeaders = captureSent(headers);
|
|
1324
1502
|
fetchResp = await doFetch(headers);
|
|
1325
1503
|
}
|
|
1326
|
-
const responseBody = await fetchResp.text();
|
|
1327
1504
|
const rawHeaders = {};
|
|
1328
1505
|
fetchResp.headers.forEach((value, key) => {
|
|
1329
1506
|
rawHeaders[key] = value;
|
|
1330
1507
|
});
|
|
1508
|
+
const kind = detectStreamKind(rawHeaders["content-type"], opts.forceStream);
|
|
1509
|
+
const bodyStream = fetchResp.body;
|
|
1510
|
+
const canStream = kind !== null && bodyStream !== null && typeof bodyStream.getReader === "function";
|
|
1511
|
+
let responseBody;
|
|
1512
|
+
let streamFields = {};
|
|
1513
|
+
if (canStream) {
|
|
1514
|
+
const r = await readStream(bodyStream, kind, start, {
|
|
1515
|
+
signal: opts.signal,
|
|
1516
|
+
onEvent: opts.onStreamEvent,
|
|
1517
|
+
idleMs: req.stream?.idleMs,
|
|
1518
|
+
maxMs: req.stream?.maxMs
|
|
1519
|
+
});
|
|
1520
|
+
responseBody = r.text;
|
|
1521
|
+
streamFields = { streamed: true, events: r.events, streamClose: r.close, firstEventMs: r.firstEventMs };
|
|
1522
|
+
} else {
|
|
1523
|
+
responseBody = await fetchResp.text();
|
|
1524
|
+
}
|
|
1331
1525
|
return {
|
|
1332
1526
|
status: fetchResp.status,
|
|
1333
1527
|
statusText: fetchResp.statusText,
|
|
@@ -1336,7 +1530,8 @@ async function performHttpExchange(opts) {
|
|
|
1336
1530
|
durationMs: Date.now() - start,
|
|
1337
1531
|
sentHeaders,
|
|
1338
1532
|
sentBody: effectiveBody,
|
|
1339
|
-
finalUrl
|
|
1533
|
+
finalUrl,
|
|
1534
|
+
...streamFields
|
|
1340
1535
|
};
|
|
1341
1536
|
}
|
|
1342
1537
|
function deriveRunStatus(postScriptError, testResults, httpStatus) {
|