wayfarer-cli 2.1.0 → 2.2.0
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/README.md +3 -2
- package/dist/wayfarer-cli.mjs +465 -344
- package/package.json +1 -1
package/dist/wayfarer-cli.mjs
CHANGED
|
@@ -7493,9 +7493,9 @@ function isTextType(mediaType) {
|
|
|
7493
7493
|
const subtype = type.slice(type.indexOf("/") + 1);
|
|
7494
7494
|
return type.startsWith("text/") || subtype === "xml" || subtype.startsWith("xml-") || subtype.endsWith("+xml") || TEXT_WORDS.some((word) => type.includes(word));
|
|
7495
7495
|
}
|
|
7496
|
-
function
|
|
7496
|
+
function readBody(bytes, contentType2) {
|
|
7497
7497
|
if (!bytes || bytes.byteLength === 0) {
|
|
7498
|
-
return null;
|
|
7498
|
+
return { value: null };
|
|
7499
7499
|
}
|
|
7500
7500
|
const type = (contentType2 ?? "").trim();
|
|
7501
7501
|
const mediaType = type.split(";")[0].trim();
|
|
@@ -7505,21 +7505,21 @@ function decodeResponseBody(bytes, contentType2) {
|
|
|
7505
7505
|
text3 = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
7506
7506
|
} catch (error) {
|
|
7507
7507
|
if (!(error instanceof TypeError)) throw error;
|
|
7508
|
-
return new BinaryBody(bytes, "");
|
|
7508
|
+
return { value: new BinaryBody(bytes, "") };
|
|
7509
7509
|
}
|
|
7510
7510
|
} else if (JSON_TYPE.test(mediaType) || isTextType(mediaType)) {
|
|
7511
7511
|
text3 = decodeText(bytes, charsetOf(type));
|
|
7512
7512
|
} else {
|
|
7513
|
-
return new BinaryBody(bytes, mediaType);
|
|
7513
|
+
return { value: new BinaryBody(bytes, mediaType) };
|
|
7514
7514
|
}
|
|
7515
7515
|
const trimmed = text3.trim();
|
|
7516
7516
|
if (JSON_TYPE.test(mediaType) || trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
7517
7517
|
const parsed2 = parseJson(text3);
|
|
7518
7518
|
if (parsed2.ok && parsed2.value !== null && (JSON_TYPE.test(mediaType) || typeof parsed2.value === "object")) {
|
|
7519
|
-
return parsed2.value;
|
|
7519
|
+
return { value: parsed2.value, text: text3 };
|
|
7520
7520
|
}
|
|
7521
7521
|
}
|
|
7522
|
-
return text3;
|
|
7522
|
+
return { value: text3, text: text3 };
|
|
7523
7523
|
}
|
|
7524
7524
|
function charsetOf(contentType2) {
|
|
7525
7525
|
const match = /;\s*charset\s*=\s*"?([^";\s]+)"?/i.exec(contentType2);
|
|
@@ -7533,12 +7533,12 @@ function decodeText(bytes, charset) {
|
|
|
7533
7533
|
return new TextDecoder("utf-8").decode(bytes);
|
|
7534
7534
|
}
|
|
7535
7535
|
}
|
|
7536
|
-
function
|
|
7536
|
+
function readEnvelope(envelope) {
|
|
7537
7537
|
const contentType2 = envelope.headers.find(([name]) => name.toLowerCase() === "content-type")?.[1];
|
|
7538
7538
|
if (envelope.body instanceof Blob) {
|
|
7539
|
-
return new BinaryBody(envelope.body, (contentType2 ?? "").split(";")[0].trim());
|
|
7539
|
+
return { value: new BinaryBody(envelope.body, (contentType2 ?? "").split(";")[0].trim()) };
|
|
7540
7540
|
}
|
|
7541
|
-
return
|
|
7541
|
+
return readBody(envelope.body, contentType2);
|
|
7542
7542
|
}
|
|
7543
7543
|
|
|
7544
7544
|
// ../core/src/id.ts
|
|
@@ -7589,15 +7589,16 @@ function buildAuthHeaders(auth3) {
|
|
|
7589
7589
|
return { Authorization: `Bearer ${auth3.token}` };
|
|
7590
7590
|
}
|
|
7591
7591
|
if (auth3.type === "basic" && auth3.username) {
|
|
7592
|
-
|
|
7593
|
-
const encoded = btoa(String.fromCharCode(...bytes));
|
|
7594
|
-
return { Authorization: `Basic ${encoded}` };
|
|
7592
|
+
return { Authorization: basicCredentials(auth3.username, auth3.password) };
|
|
7595
7593
|
}
|
|
7596
7594
|
if (auth3.type === "apikey" && auth3.key && auth3.in === "header") {
|
|
7597
7595
|
return Object.fromEntries([[auth3.key, auth3.value]]);
|
|
7598
7596
|
}
|
|
7599
7597
|
return {};
|
|
7600
7598
|
}
|
|
7599
|
+
function basicCredentials(user, password) {
|
|
7600
|
+
return `Basic ${btoa(String.fromCharCode(...new TextEncoder().encode(`${user}:${password}`)))}`;
|
|
7601
|
+
}
|
|
7601
7602
|
function buildAuthQueryParam(auth3) {
|
|
7602
7603
|
if (auth3.type === "apikey" && auth3.key && auth3.in === "query") {
|
|
7603
7604
|
return { key: auth3.key, value: auth3.value };
|
|
@@ -7662,86 +7663,117 @@ function credentialsOf(auth3) {
|
|
|
7662
7663
|
}
|
|
7663
7664
|
}
|
|
7664
7665
|
|
|
7665
|
-
// ../core/src/
|
|
7666
|
+
// ../core/src/vault/vault-crypto.ts
|
|
7667
|
+
var KDF_ITERATIONS = 6e5;
|
|
7668
|
+
var MAX_KDF_ITERATIONS = 1e7;
|
|
7669
|
+
var IV_BYTES = 12;
|
|
7670
|
+
var AES = { name: "AES-GCM", length: 256 };
|
|
7671
|
+
var VAULT_FILE_FORMAT = "wayfarer/vault";
|
|
7672
|
+
function fromBase64Url(text3) {
|
|
7673
|
+
const binary = atob(text3.replace(/-/g, "+").replace(/_/g, "/"));
|
|
7674
|
+
return Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
7675
|
+
}
|
|
7666
7676
|
var utf8 = (text3) => new TextEncoder().encode(text3);
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7677
|
+
async function deriveKek(passphrase, kdf) {
|
|
7678
|
+
if (!crypto.subtle) {
|
|
7679
|
+
throw new Error("The vault needs a secure page: open Wayfarer over https or on localhost.");
|
|
7680
|
+
}
|
|
7681
|
+
const material = await crypto.subtle.importKey("raw", utf8(passphrase), "PBKDF2", false, ["deriveKey"]);
|
|
7682
|
+
return crypto.subtle.deriveKey(
|
|
7683
|
+
{ name: "PBKDF2", salt: fromBase64Url(kdf.salt), iterations: kdf.iterations, hash: "SHA-256" },
|
|
7684
|
+
material,
|
|
7685
|
+
AES,
|
|
7686
|
+
false,
|
|
7687
|
+
["wrapKey", "unwrapKey"]
|
|
7688
|
+
);
|
|
7689
|
+
}
|
|
7690
|
+
async function unwrap(record, passphrase, extractable) {
|
|
7691
|
+
const bytes = fromBase64Url(record.wrappedDek);
|
|
7692
|
+
const kek = await deriveKek(passphrase, record.kdf);
|
|
7678
7693
|
try {
|
|
7679
|
-
return
|
|
7694
|
+
return await crypto.subtle.unwrapKey(
|
|
7695
|
+
"raw",
|
|
7696
|
+
bytes.slice(IV_BYTES),
|
|
7697
|
+
kek,
|
|
7698
|
+
{ name: "AES-GCM", iv: bytes.slice(0, IV_BYTES) },
|
|
7699
|
+
AES,
|
|
7700
|
+
extractable,
|
|
7701
|
+
["encrypt", "decrypt"]
|
|
7702
|
+
);
|
|
7680
7703
|
} catch (error) {
|
|
7681
|
-
if (
|
|
7682
|
-
|
|
7704
|
+
if (error instanceof Error && error.name === "OperationError") return null;
|
|
7705
|
+
throw error;
|
|
7683
7706
|
}
|
|
7684
|
-
}
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
const
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
return [
|
|
7694
|
-
encode(decode(cut === -1 ? pair : pair.slice(0, cut))),
|
|
7695
|
-
encode(decode(cut === -1 ? "" : pair.slice(cut + 1)))
|
|
7696
|
-
];
|
|
7697
|
-
}).sort(
|
|
7698
|
-
([keyA, valueA], [keyB, valueB]) => keyA === keyB ? valueA < valueB ? -1 : valueA > valueB ? 1 : 0 : keyA < keyB ? -1 : 1
|
|
7699
|
-
).map(([key2, value]) => `${key2}=${value}`).join("&");
|
|
7700
|
-
const date = at.toISOString().replace(/[-:]|\.\d{3}/g, "");
|
|
7701
|
-
const day = date.slice(0, 8);
|
|
7702
|
-
const body3 = typeof request.body === "string" ? utf8(request.body) : new Uint8Array(request.body ?? []);
|
|
7703
|
-
const bodyHash = hex(await sha256(body3));
|
|
7704
|
-
const added2 = [
|
|
7705
|
-
["X-Amz-Date", date],
|
|
7706
|
-
...credentials.sessionToken ? [["X-Amz-Security-Token", credentials.sessionToken]] : [],
|
|
7707
|
-
...s3 ? [["X-Amz-Content-Sha256", bodyHash]] : []
|
|
7708
|
-
];
|
|
7709
|
-
const byName = /* @__PURE__ */ new Map();
|
|
7710
|
-
const own3 = request.headers.filter(
|
|
7711
|
-
([name]) => !added2.some(([written]) => written.toLowerCase() === name.toLowerCase()) && name.toLowerCase() !== "host"
|
|
7707
|
+
}
|
|
7708
|
+
function unwrapDek(record, passphrase) {
|
|
7709
|
+
return unwrap(record, passphrase, false);
|
|
7710
|
+
}
|
|
7711
|
+
async function decryptSecret(dek, id, envelope) {
|
|
7712
|
+
const plain = await crypto.subtle.decrypt(
|
|
7713
|
+
{ name: "AES-GCM", iv: fromBase64Url(envelope.iv), additionalData: utf8(id) },
|
|
7714
|
+
dek,
|
|
7715
|
+
fromBase64Url(envelope.ct)
|
|
7712
7716
|
);
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7717
|
+
return new TextDecoder().decode(plain);
|
|
7718
|
+
}
|
|
7719
|
+
function field(value, key) {
|
|
7720
|
+
return typeof value === "object" && value !== null && Object.hasOwn(value, key) ? value[key] : void 0;
|
|
7721
|
+
}
|
|
7722
|
+
var isBase64Url = (value) => typeof value === "string" && /^[\w-]+$/.test(value);
|
|
7723
|
+
function validateVaultFile(value) {
|
|
7724
|
+
const issues = [];
|
|
7725
|
+
const fail = (path, message) => issues.push({ path, message });
|
|
7726
|
+
if (typeof value !== "object" || value === null) {
|
|
7727
|
+
fail("$", "Value must be an object.");
|
|
7728
|
+
return issues;
|
|
7716
7729
|
}
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
|
|
7727
|
-
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
7735
|
-
|
|
7736
|
-
|
|
7737
|
-
|
|
7738
|
-
|
|
7739
|
-
|
|
7740
|
-
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
|
|
7744
|
-
|
|
7730
|
+
if (field(value, "$id") !== VAULT_FILE_FORMAT) {
|
|
7731
|
+
fail("$id", `Not a Wayfarer vault file: "$id" must be "${VAULT_FILE_FORMAT}".`);
|
|
7732
|
+
return issues;
|
|
7733
|
+
}
|
|
7734
|
+
const vault = field(value, "vault");
|
|
7735
|
+
if (typeof vault !== "object" || vault === null) {
|
|
7736
|
+
fail("vault", "Value must be an object.");
|
|
7737
|
+
} else {
|
|
7738
|
+
const kdf = field(vault, "kdf");
|
|
7739
|
+
if (typeof kdf !== "object" || kdf === null) {
|
|
7740
|
+
fail("vault.kdf", "Value must be an object.");
|
|
7741
|
+
} else {
|
|
7742
|
+
if (field(kdf, "alg") !== "PBKDF2-SHA256") fail("vault.kdf.alg", 'Value must be "PBKDF2-SHA256".');
|
|
7743
|
+
const iterations = field(kdf, "iterations");
|
|
7744
|
+
if (!(Number.isInteger(iterations) && iterations >= KDF_ITERATIONS && iterations <= MAX_KDF_ITERATIONS)) {
|
|
7745
|
+
fail("vault.kdf.iterations", `Value must be a whole number from ${KDF_ITERATIONS} to ${MAX_KDF_ITERATIONS}.`);
|
|
7746
|
+
}
|
|
7747
|
+
if (!isBase64Url(field(kdf, "salt"))) fail("vault.kdf.salt", "Value must be base64url text.");
|
|
7748
|
+
}
|
|
7749
|
+
if (!isBase64Url(field(vault, "wrappedDek"))) fail("vault.wrappedDek", "Value must be base64url text.");
|
|
7750
|
+
}
|
|
7751
|
+
const secrets = field(value, "secrets");
|
|
7752
|
+
if (!Array.isArray(secrets)) {
|
|
7753
|
+
fail("secrets", "Value must be an array.");
|
|
7754
|
+
return issues;
|
|
7755
|
+
}
|
|
7756
|
+
secrets.forEach((secret, index) => {
|
|
7757
|
+
const path = `secrets[${index}]`;
|
|
7758
|
+
if (typeof secret !== "object" || secret === null) {
|
|
7759
|
+
fail(path, "Value must be an object.");
|
|
7760
|
+
return;
|
|
7761
|
+
}
|
|
7762
|
+
const id = field(secret, "id");
|
|
7763
|
+
if (typeof id !== "string" || !id) fail(`${path}.id`, "Value must be a non-empty string.");
|
|
7764
|
+
if (typeof field(secret, "name") !== "string") fail(`${path}.name`, "Value must be a string.");
|
|
7765
|
+
const environmentId = field(secret, "environmentId");
|
|
7766
|
+
if (environmentId !== void 0 && typeof environmentId !== "string")
|
|
7767
|
+
fail(`${path}.environmentId`, "Value must be a string.");
|
|
7768
|
+
const envelope = field(secret, "envelope");
|
|
7769
|
+
if (typeof envelope !== "object" || envelope === null) {
|
|
7770
|
+
fail(`${path}.envelope`, "Value must be an object.");
|
|
7771
|
+
return;
|
|
7772
|
+
}
|
|
7773
|
+
if (!isBase64Url(field(envelope, "iv"))) fail(`${path}.envelope.iv`, "Value must be base64url text.");
|
|
7774
|
+
if (!isBase64Url(field(envelope, "ct"))) fail(`${path}.envelope.ct`, "Value must be base64url text.");
|
|
7775
|
+
});
|
|
7776
|
+
return issues;
|
|
7745
7777
|
}
|
|
7746
7778
|
|
|
7747
7779
|
// ../core/src/auth/oauth2.ts
|
|
@@ -7772,12 +7804,42 @@ function folderChain(folders, folderId) {
|
|
|
7772
7804
|
}
|
|
7773
7805
|
return chain;
|
|
7774
7806
|
}
|
|
7775
|
-
function childrenInOrder(tree
|
|
7776
|
-
const
|
|
7777
|
-
|
|
7778
|
-
|
|
7807
|
+
function childrenInOrder(tree) {
|
|
7808
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
7809
|
+
const add = (parent, entry) => {
|
|
7810
|
+
const siblings = byParent.get(parent);
|
|
7811
|
+
if (siblings) siblings.push(entry);
|
|
7812
|
+
else byParent.set(parent, [entry]);
|
|
7813
|
+
};
|
|
7814
|
+
for (const folder of tree.folders)
|
|
7815
|
+
add(folder.parentFolderId, { order: folder.order, kind: 0, id: folder.id, child: { folder } });
|
|
7816
|
+
for (const request of tree.requests)
|
|
7817
|
+
add(request.folderId, { order: request.order, kind: 1, id: request.id, child: { request } });
|
|
7818
|
+
const sorted = new Map(
|
|
7819
|
+
[...byParent].map(([parent, entries]) => [
|
|
7820
|
+
parent,
|
|
7821
|
+
entries.sort((a, b) => a.order - b.order || a.kind - b.kind || a.id.localeCompare(b.id)).map((entry) => entry.child)
|
|
7822
|
+
])
|
|
7823
|
+
);
|
|
7824
|
+
return (parent) => sorted.get(parent) ?? [];
|
|
7825
|
+
}
|
|
7826
|
+
function treeInOrder(tree) {
|
|
7827
|
+
const entered = /* @__PURE__ */ new Set();
|
|
7828
|
+
const childrenOf = childrenInOrder(tree);
|
|
7829
|
+
const walk2 = (parent) => childrenOf(parent).flatMap((child) => {
|
|
7830
|
+
if (child.request !== void 0) return [{ kind: "request", request: child.request }];
|
|
7831
|
+
const { folder } = child;
|
|
7832
|
+
if (!folder || entered.has(folder.id)) return [];
|
|
7833
|
+
entered.add(folder.id);
|
|
7834
|
+
return [{ kind: "folder", folder }, ...walk2(folder.id)];
|
|
7835
|
+
});
|
|
7836
|
+
const listed = walk2(void 0);
|
|
7837
|
+
const seen = new Set(listed.map((item) => item.kind === "request" ? item.request : item.folder));
|
|
7838
|
+
return [
|
|
7839
|
+
...listed,
|
|
7840
|
+
...tree.folders.filter((folder) => !seen.has(folder)).map((folder) => ({ kind: "folder", folder })),
|
|
7841
|
+
...tree.requests.filter((request) => !seen.has(request)).map((request) => ({ kind: "request", request }))
|
|
7779
7842
|
];
|
|
7780
|
-
return entries.sort((a, b) => a.order - b.order || a.kind - b.kind || a.id.localeCompare(b.id)).map((entry) => entry.child);
|
|
7781
7843
|
}
|
|
7782
7844
|
function ancestorsOf(collection, folders, folderId) {
|
|
7783
7845
|
const named2 = (kind, { name, variables: variables2, auth: auth3, scripts: scripts2 }) => ({
|
|
@@ -7844,7 +7906,7 @@ function emptyRequest() {
|
|
|
7844
7906
|
function isObject(value) {
|
|
7845
7907
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7846
7908
|
}
|
|
7847
|
-
function
|
|
7909
|
+
function field2(object, key) {
|
|
7848
7910
|
return Object.hasOwn(object, key) ? object[key] : void 0;
|
|
7849
7911
|
}
|
|
7850
7912
|
var Checker = class {
|
|
@@ -7858,12 +7920,12 @@ var Checker = class {
|
|
|
7858
7920
|
return null;
|
|
7859
7921
|
}
|
|
7860
7922
|
string(object, key, path, optional = false) {
|
|
7861
|
-
const value =
|
|
7923
|
+
const value = field2(object, key);
|
|
7862
7924
|
if (typeof value === "string" || optional && value === void 0) return;
|
|
7863
7925
|
this.fail(`${path}.${key}`, "Value must be a string.");
|
|
7864
7926
|
}
|
|
7865
7927
|
oneOf(object, key, path, allowed, optional = false) {
|
|
7866
|
-
const value =
|
|
7928
|
+
const value = field2(object, key);
|
|
7867
7929
|
if (optional && value === void 0 || typeof value === "string" && allowed.includes(value)) return;
|
|
7868
7930
|
this.fail(`${path}.${key}`, `Value must be one of ${allowed.join(", ")}.`);
|
|
7869
7931
|
}
|
|
@@ -7879,7 +7941,7 @@ var Checker = class {
|
|
|
7879
7941
|
});
|
|
7880
7942
|
}
|
|
7881
7943
|
enabled(row, rowPath) {
|
|
7882
|
-
if (typeof
|
|
7944
|
+
if (typeof field2(row, "enabled") !== "boolean") {
|
|
7883
7945
|
this.fail(`${rowPath}.enabled`, "Value must be true or false.");
|
|
7884
7946
|
}
|
|
7885
7947
|
}
|
|
@@ -7888,7 +7950,7 @@ var Checker = class {
|
|
|
7888
7950
|
this.string(row, "key", rowPath);
|
|
7889
7951
|
this.string(row, "value", rowPath);
|
|
7890
7952
|
this.enabled(row, rowPath);
|
|
7891
|
-
if (Object.hasOwn(row, "json") && (
|
|
7953
|
+
if (Object.hasOwn(row, "json") && (field2(row, "json") !== true || !isJsonText(field2(row, "value"))))
|
|
7892
7954
|
this.fail(`${rowPath}.json`, "Value must be true, on a value that is JSON.");
|
|
7893
7955
|
});
|
|
7894
7956
|
}
|
|
@@ -7897,7 +7959,7 @@ var Checker = class {
|
|
|
7897
7959
|
const auth3 = this.object(value, path);
|
|
7898
7960
|
if (!auth3) return;
|
|
7899
7961
|
this.oneOf(auth3, "type", path, inherit ? AUTH_TYPES : AUTH_TYPES.filter((type2) => type2 !== "inherit"));
|
|
7900
|
-
const type =
|
|
7962
|
+
const type = field2(auth3, "type");
|
|
7901
7963
|
if (type === "bearer") {
|
|
7902
7964
|
this.string(auth3, "token", path);
|
|
7903
7965
|
} else if (type === "basic") {
|
|
@@ -7914,7 +7976,7 @@ var Checker = class {
|
|
|
7914
7976
|
for (const key of ["authUrl", "tokenUrl", "clientId", "clientSecret", "scope", "username", "password"])
|
|
7915
7977
|
this.string(auth3, key, path);
|
|
7916
7978
|
this.oneOf(auth3, "clientAuth", path, ["header", "body"]);
|
|
7917
|
-
if (typeof
|
|
7979
|
+
if (typeof field2(auth3, "retryOn401") !== "boolean")
|
|
7918
7980
|
this.fail(`${path}.retryOn401`, "Value must be true or false.");
|
|
7919
7981
|
}
|
|
7920
7982
|
}
|
|
@@ -7938,9 +8000,9 @@ function validateInherited(value, path, kind) {
|
|
|
7938
8000
|
const check = new Checker();
|
|
7939
8001
|
const node = check.object(value, path);
|
|
7940
8002
|
if (!node) return check.issues;
|
|
7941
|
-
check.auth(
|
|
7942
|
-
check.scripts(
|
|
7943
|
-
if (kind === "folder") check.rows(
|
|
8003
|
+
check.auth(field2(node, "auth"), `${path}.auth`, kind === "folder");
|
|
8004
|
+
check.scripts(field2(node, "scripts"), `${path}.scripts`);
|
|
8005
|
+
if (kind === "folder") check.rows(field2(node, "variables"), `${path}.variables`);
|
|
7944
8006
|
else check.oneOf(node, "route", path, ["auto", "direct", "bridge"], true);
|
|
7945
8007
|
return check.issues;
|
|
7946
8008
|
}
|
|
@@ -7948,75 +8010,75 @@ function validateRequestContent(value, path) {
|
|
|
7948
8010
|
const check = new Checker();
|
|
7949
8011
|
const request = check.object(value, path);
|
|
7950
8012
|
if (!request) return check.issues;
|
|
7951
|
-
const method =
|
|
8013
|
+
const method = field2(request, "method");
|
|
7952
8014
|
if (typeof method !== "string" || !isHttpMethod(method)) {
|
|
7953
8015
|
check.fail(`${path}.method`, "Value must be an HTTP method: one word of at most 32 characters, in upper case.");
|
|
7954
8016
|
}
|
|
7955
|
-
if (typeof
|
|
8017
|
+
if (typeof field2(request, "url") !== "string" || !field2(request, "url").trim()) {
|
|
7956
8018
|
check.fail(`${path}.url`, "Value must be a non-empty string.");
|
|
7957
8019
|
}
|
|
7958
|
-
check.rows(
|
|
7959
|
-
check.rows(
|
|
7960
|
-
const body3 = check.object(
|
|
8020
|
+
check.rows(field2(request, "params"), `${path}.params`);
|
|
8021
|
+
check.rows(field2(request, "headers"), `${path}.headers`);
|
|
8022
|
+
const body3 = check.object(field2(request, "body"), `${path}.body`);
|
|
7961
8023
|
if (body3) {
|
|
7962
|
-
const mode =
|
|
8024
|
+
const mode = field2(body3, "mode");
|
|
7963
8025
|
check.oneOf(body3, "mode", `${path}.body`, BODY_MODES);
|
|
7964
|
-
const part = (name) =>
|
|
8026
|
+
const part = (name) => field2(body3, name) !== void 0 || mode === name ? name : null;
|
|
7965
8027
|
if (part("raw")) {
|
|
7966
|
-
const raw = check.object(
|
|
8028
|
+
const raw = check.object(field2(body3, "raw"), `${path}.body.raw`);
|
|
7967
8029
|
if (raw) {
|
|
7968
8030
|
check.oneOf(raw, "language", `${path}.body.raw`, RAW_LANGUAGES);
|
|
7969
8031
|
check.string(raw, "text", `${path}.body.raw`);
|
|
7970
8032
|
}
|
|
7971
8033
|
}
|
|
7972
8034
|
if (part("urlencoded")) {
|
|
7973
|
-
check.rows(
|
|
8035
|
+
check.rows(field2(body3, "urlencoded"), `${path}.body.urlencoded`);
|
|
7974
8036
|
}
|
|
7975
8037
|
if (part("multipart")) {
|
|
7976
|
-
check.list(
|
|
8038
|
+
check.list(field2(body3, "multipart"), `${path}.body.multipart`, (item, itemPath) => {
|
|
7977
8039
|
check.string(item, "key", itemPath);
|
|
7978
8040
|
check.enabled(item, itemPath);
|
|
7979
8041
|
check.oneOf(item, "kind", itemPath, ["text", "file"]);
|
|
7980
|
-
if (
|
|
7981
|
-
if (
|
|
8042
|
+
if (field2(item, "kind") === "text") check.string(item, "value", itemPath);
|
|
8043
|
+
if (field2(item, "kind") === "file") check.fileRef(item, itemPath);
|
|
7982
8044
|
});
|
|
7983
8045
|
}
|
|
7984
8046
|
if (part("graphql")) {
|
|
7985
|
-
const graphql = check.object(
|
|
8047
|
+
const graphql = check.object(field2(body3, "graphql"), `${path}.body.graphql`);
|
|
7986
8048
|
if (graphql) {
|
|
7987
8049
|
check.string(graphql, "query", `${path}.body.graphql`);
|
|
7988
8050
|
check.string(graphql, "variables", `${path}.body.graphql`);
|
|
7989
8051
|
}
|
|
7990
8052
|
}
|
|
7991
|
-
if (
|
|
7992
|
-
const binary = check.object(
|
|
8053
|
+
if (field2(body3, "binary") !== void 0) {
|
|
8054
|
+
const binary = check.object(field2(body3, "binary"), `${path}.body.binary`);
|
|
7993
8055
|
if (binary) {
|
|
7994
8056
|
check.fileRef(binary, `${path}.body.binary`);
|
|
7995
8057
|
check.string(binary, "contentType", `${path}.body.binary`, true);
|
|
7996
8058
|
}
|
|
7997
8059
|
}
|
|
7998
8060
|
}
|
|
7999
|
-
check.auth(
|
|
8000
|
-
check.scripts(
|
|
8001
|
-
check.list(
|
|
8061
|
+
check.auth(field2(request, "auth"), `${path}.auth`, true);
|
|
8062
|
+
check.scripts(field2(request, "scripts"), `${path}.scripts`);
|
|
8063
|
+
check.list(field2(request, "tests"), `${path}.tests`, (test, testPath) => {
|
|
8002
8064
|
check.string(test, "id", testPath);
|
|
8003
8065
|
check.oneOf(test, "target", testPath, ASSERTION_TARGETS);
|
|
8004
8066
|
check.oneOf(test, "operator", testPath, ASSERTION_OPERATORS);
|
|
8005
8067
|
check.string(test, "key", testPath, true);
|
|
8006
8068
|
check.string(test, "expected", testPath, true);
|
|
8007
8069
|
});
|
|
8008
|
-
const settings = check.object(
|
|
8070
|
+
const settings = check.object(field2(request, "settings"), `${path}.settings`);
|
|
8009
8071
|
if (settings) {
|
|
8010
|
-
const timeout =
|
|
8072
|
+
const timeout = field2(settings, "timeoutMs");
|
|
8011
8073
|
if (timeout !== void 0 && !(typeof timeout === "number" && Number.isFinite(timeout) && timeout >= 0)) {
|
|
8012
8074
|
check.fail(`${path}.settings.timeoutMs`, "Value must be a number, 0 or more.");
|
|
8013
8075
|
}
|
|
8014
|
-
const follow =
|
|
8076
|
+
const follow = field2(settings, "followRedirects");
|
|
8015
8077
|
if (follow !== void 0 && typeof follow !== "boolean") {
|
|
8016
8078
|
check.fail(`${path}.settings.followRedirects`, "Value must be true or false.");
|
|
8017
8079
|
}
|
|
8018
8080
|
check.oneOf(settings, "route", `${path}.settings`, ["auto", "direct", "bridge"], true);
|
|
8019
|
-
const insecure =
|
|
8081
|
+
const insecure = field2(settings, "tlsInsecure");
|
|
8020
8082
|
if (insecure !== void 0 && typeof insecure !== "boolean") {
|
|
8021
8083
|
check.fail(`${path}.settings.tlsInsecure`, "Value must be true or false.");
|
|
8022
8084
|
}
|
|
@@ -8110,21 +8172,10 @@ function buildPlan(payload, duplicate) {
|
|
|
8110
8172
|
action
|
|
8111
8173
|
}
|
|
8112
8174
|
];
|
|
8113
|
-
for (const
|
|
8114
|
-
entries.push(
|
|
8115
|
-
type: "folder",
|
|
8116
|
-
|
|
8117
|
-
id: folder.meta.id,
|
|
8118
|
-
action
|
|
8119
|
-
});
|
|
8120
|
-
}
|
|
8121
|
-
for (const request of payload.requests) {
|
|
8122
|
-
entries.push({
|
|
8123
|
-
type: "request",
|
|
8124
|
-
name: request.name || request.url,
|
|
8125
|
-
id: request.meta.id,
|
|
8126
|
-
action
|
|
8127
|
-
});
|
|
8175
|
+
for (const child of treeInOrder(payload)) {
|
|
8176
|
+
entries.push(
|
|
8177
|
+
child.kind === "request" ? { type: "request", name: child.request.name || child.request.url, id: child.request.meta.id, action } : { type: "folder", name: child.folder.name, id: child.folder.meta.id, action }
|
|
8178
|
+
);
|
|
8128
8179
|
}
|
|
8129
8180
|
return entries;
|
|
8130
8181
|
}
|
|
@@ -8532,119 +8583,6 @@ function scriptVariables(rows2) {
|
|
|
8532
8583
|
return [...byName.values()];
|
|
8533
8584
|
}
|
|
8534
8585
|
|
|
8535
|
-
// ../core/src/vault/vault-crypto.ts
|
|
8536
|
-
var KDF_ITERATIONS = 6e5;
|
|
8537
|
-
var MAX_KDF_ITERATIONS = 1e7;
|
|
8538
|
-
var IV_BYTES = 12;
|
|
8539
|
-
var AES = { name: "AES-GCM", length: 256 };
|
|
8540
|
-
var VAULT_FILE_FORMAT = "wayfarer/vault";
|
|
8541
|
-
function fromBase64Url(text3) {
|
|
8542
|
-
const binary = atob(text3.replace(/-/g, "+").replace(/_/g, "/"));
|
|
8543
|
-
return Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
8544
|
-
}
|
|
8545
|
-
var utf82 = (text3) => new TextEncoder().encode(text3);
|
|
8546
|
-
async function deriveKek(passphrase, kdf) {
|
|
8547
|
-
if (!crypto.subtle) {
|
|
8548
|
-
throw new Error("The vault needs a secure page: open Wayfarer over https or on localhost.");
|
|
8549
|
-
}
|
|
8550
|
-
const material = await crypto.subtle.importKey("raw", utf82(passphrase), "PBKDF2", false, ["deriveKey"]);
|
|
8551
|
-
return crypto.subtle.deriveKey(
|
|
8552
|
-
{ name: "PBKDF2", salt: fromBase64Url(kdf.salt), iterations: kdf.iterations, hash: "SHA-256" },
|
|
8553
|
-
material,
|
|
8554
|
-
AES,
|
|
8555
|
-
false,
|
|
8556
|
-
["wrapKey", "unwrapKey"]
|
|
8557
|
-
);
|
|
8558
|
-
}
|
|
8559
|
-
async function unwrap(record, passphrase, extractable) {
|
|
8560
|
-
const bytes = fromBase64Url(record.wrappedDek);
|
|
8561
|
-
const kek = await deriveKek(passphrase, record.kdf);
|
|
8562
|
-
try {
|
|
8563
|
-
return await crypto.subtle.unwrapKey(
|
|
8564
|
-
"raw",
|
|
8565
|
-
bytes.slice(IV_BYTES),
|
|
8566
|
-
kek,
|
|
8567
|
-
{ name: "AES-GCM", iv: bytes.slice(0, IV_BYTES) },
|
|
8568
|
-
AES,
|
|
8569
|
-
extractable,
|
|
8570
|
-
["encrypt", "decrypt"]
|
|
8571
|
-
);
|
|
8572
|
-
} catch (error) {
|
|
8573
|
-
if (error instanceof Error && error.name === "OperationError") return null;
|
|
8574
|
-
throw error;
|
|
8575
|
-
}
|
|
8576
|
-
}
|
|
8577
|
-
function unwrapDek(record, passphrase) {
|
|
8578
|
-
return unwrap(record, passphrase, false);
|
|
8579
|
-
}
|
|
8580
|
-
async function decryptSecret(dek, id, envelope) {
|
|
8581
|
-
const plain = await crypto.subtle.decrypt(
|
|
8582
|
-
{ name: "AES-GCM", iv: fromBase64Url(envelope.iv), additionalData: utf82(id) },
|
|
8583
|
-
dek,
|
|
8584
|
-
fromBase64Url(envelope.ct)
|
|
8585
|
-
);
|
|
8586
|
-
return new TextDecoder().decode(plain);
|
|
8587
|
-
}
|
|
8588
|
-
function field2(value, key) {
|
|
8589
|
-
return typeof value === "object" && value !== null && Object.hasOwn(value, key) ? value[key] : void 0;
|
|
8590
|
-
}
|
|
8591
|
-
var isBase64Url = (value) => typeof value === "string" && /^[\w-]+$/.test(value);
|
|
8592
|
-
function validateVaultFile(value) {
|
|
8593
|
-
const issues = [];
|
|
8594
|
-
const fail = (path, message) => issues.push({ path, message });
|
|
8595
|
-
if (typeof value !== "object" || value === null) {
|
|
8596
|
-
fail("$", "Value must be an object.");
|
|
8597
|
-
return issues;
|
|
8598
|
-
}
|
|
8599
|
-
if (field2(value, "$id") !== VAULT_FILE_FORMAT) {
|
|
8600
|
-
fail("$id", `Not a Wayfarer vault file: "$id" must be "${VAULT_FILE_FORMAT}".`);
|
|
8601
|
-
return issues;
|
|
8602
|
-
}
|
|
8603
|
-
const vault = field2(value, "vault");
|
|
8604
|
-
if (typeof vault !== "object" || vault === null) {
|
|
8605
|
-
fail("vault", "Value must be an object.");
|
|
8606
|
-
} else {
|
|
8607
|
-
const kdf = field2(vault, "kdf");
|
|
8608
|
-
if (typeof kdf !== "object" || kdf === null) {
|
|
8609
|
-
fail("vault.kdf", "Value must be an object.");
|
|
8610
|
-
} else {
|
|
8611
|
-
if (field2(kdf, "alg") !== "PBKDF2-SHA256") fail("vault.kdf.alg", 'Value must be "PBKDF2-SHA256".');
|
|
8612
|
-
const iterations = field2(kdf, "iterations");
|
|
8613
|
-
if (!(Number.isInteger(iterations) && iterations >= KDF_ITERATIONS && iterations <= MAX_KDF_ITERATIONS)) {
|
|
8614
|
-
fail("vault.kdf.iterations", `Value must be a whole number from ${KDF_ITERATIONS} to ${MAX_KDF_ITERATIONS}.`);
|
|
8615
|
-
}
|
|
8616
|
-
if (!isBase64Url(field2(kdf, "salt"))) fail("vault.kdf.salt", "Value must be base64url text.");
|
|
8617
|
-
}
|
|
8618
|
-
if (!isBase64Url(field2(vault, "wrappedDek"))) fail("vault.wrappedDek", "Value must be base64url text.");
|
|
8619
|
-
}
|
|
8620
|
-
const secrets = field2(value, "secrets");
|
|
8621
|
-
if (!Array.isArray(secrets)) {
|
|
8622
|
-
fail("secrets", "Value must be an array.");
|
|
8623
|
-
return issues;
|
|
8624
|
-
}
|
|
8625
|
-
secrets.forEach((secret, index) => {
|
|
8626
|
-
const path = `secrets[${index}]`;
|
|
8627
|
-
if (typeof secret !== "object" || secret === null) {
|
|
8628
|
-
fail(path, "Value must be an object.");
|
|
8629
|
-
return;
|
|
8630
|
-
}
|
|
8631
|
-
const id = field2(secret, "id");
|
|
8632
|
-
if (typeof id !== "string" || !id) fail(`${path}.id`, "Value must be a non-empty string.");
|
|
8633
|
-
if (typeof field2(secret, "name") !== "string") fail(`${path}.name`, "Value must be a string.");
|
|
8634
|
-
const environmentId = field2(secret, "environmentId");
|
|
8635
|
-
if (environmentId !== void 0 && typeof environmentId !== "string")
|
|
8636
|
-
fail(`${path}.environmentId`, "Value must be a string.");
|
|
8637
|
-
const envelope = field2(secret, "envelope");
|
|
8638
|
-
if (typeof envelope !== "object" || envelope === null) {
|
|
8639
|
-
fail(`${path}.envelope`, "Value must be an object.");
|
|
8640
|
-
return;
|
|
8641
|
-
}
|
|
8642
|
-
if (!isBase64Url(field2(envelope, "iv"))) fail(`${path}.envelope.iv`, "Value must be base64url text.");
|
|
8643
|
-
if (!isBase64Url(field2(envelope, "ct"))) fail(`${path}.envelope.ct`, "Value must be base64url text.");
|
|
8644
|
-
});
|
|
8645
|
-
return issues;
|
|
8646
|
-
}
|
|
8647
|
-
|
|
8648
8586
|
// ../core/src/variables/resolver.ts
|
|
8649
8587
|
var VARIABLE_SCOPES = ["local", "data", "environment", "folder", "collection", "global"];
|
|
8650
8588
|
var MAX_VARIABLE_DEPTH = 10;
|
|
@@ -8904,13 +8842,15 @@ var SseParser = class {
|
|
|
8904
8842
|
// ../core/src/transport/transport.ts
|
|
8905
8843
|
var STREAM_BODY_BYTES = 1024 * 1024;
|
|
8906
8844
|
var TransportError = class extends Error {
|
|
8907
|
-
constructor(kind, message, status) {
|
|
8845
|
+
constructor(kind, message, status, responded = false) {
|
|
8908
8846
|
super(message);
|
|
8909
8847
|
this.kind = kind;
|
|
8910
8848
|
this.status = status;
|
|
8849
|
+
this.responded = responded;
|
|
8911
8850
|
}
|
|
8912
8851
|
kind;
|
|
8913
8852
|
status;
|
|
8853
|
+
responded;
|
|
8914
8854
|
name = "TransportError";
|
|
8915
8855
|
};
|
|
8916
8856
|
var DISPLAY_CAP_BYTES = 50 * 1024 * 1024;
|
|
@@ -8920,10 +8860,13 @@ async function withDeadline(options, run) {
|
|
|
8920
8860
|
const cancel = () => controller.abort();
|
|
8921
8861
|
if (options.signal.aborted) cancel();
|
|
8922
8862
|
options.signal.addEventListener("abort", cancel);
|
|
8923
|
-
const timer = options.timeoutMs > 0 ? setTimeout(
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
|
|
8863
|
+
const timer = options.timeoutMs > 0 ? setTimeout(
|
|
8864
|
+
() => {
|
|
8865
|
+
timedOut = true;
|
|
8866
|
+
controller.abort();
|
|
8867
|
+
},
|
|
8868
|
+
Math.min(options.timeoutMs, 2 ** 31 - 1)
|
|
8869
|
+
) : void 0;
|
|
8927
8870
|
try {
|
|
8928
8871
|
return await run(controller.signal, () => clearTimeout(timer));
|
|
8929
8872
|
} catch (cause) {
|
|
@@ -8940,7 +8883,7 @@ async function withDeadline(options, run) {
|
|
|
8940
8883
|
options.signal.removeEventListener("abort", cancel);
|
|
8941
8884
|
}
|
|
8942
8885
|
}
|
|
8943
|
-
async function
|
|
8886
|
+
async function readBody2(response, cap = DISPLAY_CAP_BYTES) {
|
|
8944
8887
|
if (!response.body) return response.arrayBuffer();
|
|
8945
8888
|
const reader2 = response.body.getReader();
|
|
8946
8889
|
let chunks = [];
|
|
@@ -8974,7 +8917,7 @@ async function readEventStream(response, signal, onEvent) {
|
|
|
8974
8917
|
const kept = new Uint8Array(STREAM_BODY_BYTES);
|
|
8975
8918
|
let size = 0;
|
|
8976
8919
|
let truncated = false;
|
|
8977
|
-
let
|
|
8920
|
+
let stopped2 = false;
|
|
8978
8921
|
const reader2 = response.body?.getReader();
|
|
8979
8922
|
try {
|
|
8980
8923
|
for (; ; ) {
|
|
@@ -8989,9 +8932,9 @@ async function readEventStream(response, signal, onEvent) {
|
|
|
8989
8932
|
}
|
|
8990
8933
|
} catch (error) {
|
|
8991
8934
|
if (!signal.aborted) throw error;
|
|
8992
|
-
|
|
8935
|
+
stopped2 = true;
|
|
8993
8936
|
}
|
|
8994
|
-
return { body: kept.slice(0, size).buffer, stream: { stopped, ...truncated && { truncated } } };
|
|
8937
|
+
return { body: kept.slice(0, size).buffer, stream: { stopped: stopped2, ...truncated && { truncated } } };
|
|
8995
8938
|
}
|
|
8996
8939
|
function networkMessage(cause) {
|
|
8997
8940
|
if (!(cause instanceof Error)) return String(cause);
|
|
@@ -9008,7 +8951,8 @@ var FetchTransport = class {
|
|
|
9008
8951
|
fetchFn;
|
|
9009
8952
|
displayCap;
|
|
9010
8953
|
send(request, options) {
|
|
9011
|
-
|
|
8954
|
+
let responded = false;
|
|
8955
|
+
const sent = withDeadline(options, async (signal, arrived) => {
|
|
9012
8956
|
const response = await this.fetchFn(request.url, {
|
|
9013
8957
|
method: request.method,
|
|
9014
8958
|
headers: request.headers,
|
|
@@ -9019,9 +8963,10 @@ var FetchTransport = class {
|
|
|
9019
8963
|
redirect: "follow",
|
|
9020
8964
|
signal
|
|
9021
8965
|
});
|
|
8966
|
+
responded = true;
|
|
9022
8967
|
const { onEvent } = options;
|
|
9023
8968
|
const events = onEvent && /^text\/event-stream\b/i.test(response.headers.get("content-type") ?? "") ? (arrived(), await readEventStream(response, signal, onEvent)) : void 0;
|
|
9024
|
-
const body3 = events?.body ?? await
|
|
8969
|
+
const body3 = events?.body ?? await readBody2(response, this.displayCap);
|
|
9025
8970
|
return {
|
|
9026
8971
|
status: response.status,
|
|
9027
8972
|
statusText: response.statusText,
|
|
@@ -9034,6 +8979,11 @@ var FetchTransport = class {
|
|
|
9034
8979
|
...events && { stream: events.stream }
|
|
9035
8980
|
};
|
|
9036
8981
|
});
|
|
8982
|
+
return sent.catch((error) => {
|
|
8983
|
+
if (responded && error instanceof TransportError && error.kind === "network")
|
|
8984
|
+
throw new TransportError("network", error.message, void 0, true);
|
|
8985
|
+
throw error;
|
|
8986
|
+
});
|
|
9037
8987
|
}
|
|
9038
8988
|
};
|
|
9039
8989
|
|
|
@@ -9736,7 +9686,7 @@ var _HMAC = class {
|
|
|
9736
9686
|
this.iHash.destroy();
|
|
9737
9687
|
}
|
|
9738
9688
|
};
|
|
9739
|
-
var
|
|
9689
|
+
var hmac = /* @__PURE__ */ (() => {
|
|
9740
9690
|
const hmac_ = ((hash, key, message) => new _HMAC(hash, key).update(message).digest());
|
|
9741
9691
|
hmac_.create = (hash, key) => new _HMAC(hash, key);
|
|
9742
9692
|
return hmac_;
|
|
@@ -10180,13 +10130,13 @@ var _SHA256 = class extends SHA2_32B {
|
|
|
10180
10130
|
super(32, SHA256_IV);
|
|
10181
10131
|
}
|
|
10182
10132
|
};
|
|
10183
|
-
var
|
|
10133
|
+
var sha256 = /* @__PURE__ */ createHasher(
|
|
10184
10134
|
() => new _SHA256(),
|
|
10185
10135
|
/* @__PURE__ */ oidNist(1)
|
|
10186
10136
|
);
|
|
10187
10137
|
|
|
10188
10138
|
// ../core/src/scripting/vm-crypto.ts
|
|
10189
|
-
var HASHES = { MD5: md5, SHA1: sha1, SHA256:
|
|
10139
|
+
var HASHES = { MD5: md5, SHA1: sha1, SHA256: sha256 };
|
|
10190
10140
|
var isVmHash = (name) => Object.hasOwn(HASHES, name);
|
|
10191
10141
|
var VM_ENCODINGS = ["utf8", "hex", "base64"];
|
|
10192
10142
|
function fromWords(json) {
|
|
@@ -10212,7 +10162,7 @@ function vmHash(name, format, payload) {
|
|
|
10212
10162
|
return toWords(HASHES[name](bytesOf(format, payload)));
|
|
10213
10163
|
}
|
|
10214
10164
|
function vmHmac(name, keyFormat, key, format, payload) {
|
|
10215
|
-
return toWords(
|
|
10165
|
+
return toWords(hmac(HASHES[name], bytesOf(keyFormat, key), bytesOf(format, payload)));
|
|
10216
10166
|
}
|
|
10217
10167
|
function vmEncode(encoding, text3) {
|
|
10218
10168
|
if (encoding === "utf8") return toWords(new TextEncoder().encode(text3));
|
|
@@ -10351,8 +10301,17 @@ async function evaluate(vm, runtime, source, context, state, deadline, limits, s
|
|
|
10351
10301
|
if (Date.now() >= deadline.at) return { message: "", limit: "timeout" };
|
|
10352
10302
|
const kind = vm.typeof(error);
|
|
10353
10303
|
if (kind === "string") return { message: vm.getString(error) };
|
|
10304
|
+
if (kind === "number" || kind === "boolean" || kind === "undefined") return { message: String(vm.dump(error)) };
|
|
10354
10305
|
if (kind !== "object") return { message: "Script failed." };
|
|
10355
|
-
if (vm.sameValue(error, vm.null))
|
|
10306
|
+
if (vm.sameValue(error, vm.null)) {
|
|
10307
|
+
const probe = vm.evalCode("({})");
|
|
10308
|
+
if (probe.error) {
|
|
10309
|
+
probe.error.dispose();
|
|
10310
|
+
return { message: "", limit: "memory" };
|
|
10311
|
+
}
|
|
10312
|
+
probe.value.dispose();
|
|
10313
|
+
return { message: "null" };
|
|
10314
|
+
}
|
|
10356
10315
|
const name = property(vm, error, "name");
|
|
10357
10316
|
const message = property(vm, error, "message") ?? "Script failed.";
|
|
10358
10317
|
if (name === "InternalError" && message === "out of memory") return { message, limit: "memory" };
|
|
@@ -10683,32 +10642,6 @@ function property(vm, object, name) {
|
|
|
10683
10642
|
}
|
|
10684
10643
|
}
|
|
10685
10644
|
|
|
10686
|
-
// ../core/src/scripting/request-view.ts
|
|
10687
|
-
function scriptRequestOf(content) {
|
|
10688
|
-
const rows2 = (list4) => list4.filter((row) => row.enabled && row.key).map((row) => [row.key, row.value]);
|
|
10689
|
-
const { body: body3 } = content;
|
|
10690
|
-
const seen = body3.mode === "raw" ? { mode: "raw", raw: body3.raw?.text ?? "" } : body3.mode === "graphql" ? { mode: "raw", raw: graphqlText(body3.graphql?.query ?? "", body3.graphql?.variables ?? "") } : body3.mode === "urlencoded" ? { mode: "urlencoded", urlencoded: rows2(body3.urlencoded ?? []) } : { mode: body3.mode };
|
|
10691
|
-
return { method: content.method, url: content.url, headers: rows2(content.headers), body: seen };
|
|
10692
|
-
}
|
|
10693
|
-
function withScriptRequest(content, request) {
|
|
10694
|
-
const rows2 = (pairs2) => pairs2.map(([key, value]) => ({ key, value, enabled: true }));
|
|
10695
|
-
const untouched = content.body.mode === "graphql" && request.body.mode === "raw" && request.body.raw === graphqlText(content.body.graphql?.query ?? "", content.body.graphql?.variables ?? "");
|
|
10696
|
-
const body3 = untouched ? content.body : request.body.mode === "raw" ? {
|
|
10697
|
-
...content.body,
|
|
10698
|
-
mode: "raw",
|
|
10699
|
-
raw: {
|
|
10700
|
-
language: content.body.mode === "graphql" ? "json" : content.body.raw?.language ?? "text",
|
|
10701
|
-
text: request.body.raw
|
|
10702
|
-
}
|
|
10703
|
-
} : request.body.mode === "urlencoded" ? { ...content.body, mode: "urlencoded", urlencoded: rows2(request.body.urlencoded) } : content.body;
|
|
10704
|
-
return { ...content, method: request.method, url: request.url, headers: rows2(request.headers), body: body3 };
|
|
10705
|
-
}
|
|
10706
|
-
function sentScriptRequest(content, sent) {
|
|
10707
|
-
const { mode } = content.body;
|
|
10708
|
-
const body3 = mode === "multipart" || mode === "binary" ? { mode } : sent.body === void 0 ? { mode: "none" } : mode === "urlencoded" ? { mode, urlencoded: [...new URLSearchParams(sent.body)] } : { mode: "raw", raw: sent.body };
|
|
10709
|
-
return { method: sent.method, url: sent.url, headers: sent.headers, body: body3 };
|
|
10710
|
-
}
|
|
10711
|
-
|
|
10712
10645
|
// ../core/src/scripting/trust.ts
|
|
10713
10646
|
function scriptsOf(holder) {
|
|
10714
10647
|
return [holder.scripts.pre, holder.scripts.post].filter((script) => script.trim());
|
|
@@ -10720,7 +10653,8 @@ function runFailed(result) {
|
|
|
10720
10653
|
}
|
|
10721
10654
|
function requestsInOrder(tree, folderId) {
|
|
10722
10655
|
const entered = /* @__PURE__ */ new Set();
|
|
10723
|
-
const
|
|
10656
|
+
const childrenOf = childrenInOrder(tree);
|
|
10657
|
+
const inOrder = (parent) => childrenOf(parent).flatMap((child) => {
|
|
10724
10658
|
if (child.request) return [child.request];
|
|
10725
10659
|
if (entered.has(child.folder.id)) return [];
|
|
10726
10660
|
entered.add(child.folder.id);
|
|
@@ -10804,26 +10738,25 @@ var SECRET_PLACEHOLDER = /\{\{\s*\$secret\./;
|
|
|
10804
10738
|
function containsSecretPlaceholder(request) {
|
|
10805
10739
|
const wire = [
|
|
10806
10740
|
request.url,
|
|
10807
|
-
|
|
10741
|
+
formDecoded(request.url),
|
|
10808
10742
|
JSON.stringify(request.headers),
|
|
10809
10743
|
...request.headers.map(([, value]) => decodeBasicCredentials(value)),
|
|
10810
10744
|
// An OAuth 2.0 token request names its client in Basic credentials that are form-encoded first (RFC 6749, 2.3.1).
|
|
10811
|
-
...request.headers.map(([, value]) =>
|
|
10745
|
+
...request.headers.map(([, value]) => formDecoded(decodeBasicCredentials(value))),
|
|
10812
10746
|
...bodyTexts(request.body)
|
|
10813
10747
|
];
|
|
10814
10748
|
return wire.some((text3) => SECRET_PLACEHOLDER.test(text3));
|
|
10815
10749
|
}
|
|
10816
|
-
function
|
|
10817
|
-
|
|
10818
|
-
|
|
10819
|
-
|
|
10820
|
-
|
|
10821
|
-
return "";
|
|
10822
|
-
}
|
|
10750
|
+
function formDecoded(text3) {
|
|
10751
|
+
return text3.replaceAll("+", " ").replace(
|
|
10752
|
+
/(?:%[0-9a-f]{2})+/gi,
|
|
10753
|
+
(run) => utf82.decode(Uint8Array.from(run.slice(1).split("%"), (hex2) => parseInt(hex2, 16)))
|
|
10754
|
+
);
|
|
10823
10755
|
}
|
|
10756
|
+
var utf82 = new TextDecoder();
|
|
10824
10757
|
function bodyTexts(body3) {
|
|
10825
10758
|
if (typeof body3 === "string") {
|
|
10826
|
-
return [body3,
|
|
10759
|
+
return [body3, formDecoded(body3)];
|
|
10827
10760
|
}
|
|
10828
10761
|
if (body3 instanceof FormData) {
|
|
10829
10762
|
return [...body3].flatMap(([name, value]) => [name, typeof value === "string" ? value : value.name]);
|
|
@@ -10841,6 +10774,32 @@ function decodeBasicCredentials(headerValue) {
|
|
|
10841
10774
|
}
|
|
10842
10775
|
}
|
|
10843
10776
|
|
|
10777
|
+
// ../core/src/scripting/request-view.ts
|
|
10778
|
+
function scriptRequestOf(content) {
|
|
10779
|
+
const rows2 = (list4) => list4.filter((row) => row.enabled && row.key).map((row) => [row.key, row.value]);
|
|
10780
|
+
const { body: body3 } = content;
|
|
10781
|
+
const seen = body3.mode === "raw" ? { mode: "raw", raw: body3.raw?.text ?? "" } : body3.mode === "graphql" ? { mode: "raw", raw: graphqlText(body3.graphql?.query ?? "", body3.graphql?.variables ?? "") } : body3.mode === "urlencoded" ? { mode: "urlencoded", urlencoded: rows2(body3.urlencoded ?? []) } : { mode: body3.mode };
|
|
10782
|
+
return { method: content.method, url: content.url, headers: rows2(content.headers), body: seen };
|
|
10783
|
+
}
|
|
10784
|
+
function withScriptRequest(content, request) {
|
|
10785
|
+
const rows2 = (pairs2) => pairs2.map(([key, value]) => ({ key, value, enabled: true }));
|
|
10786
|
+
const untouched = content.body.mode === "graphql" && request.body.mode === "raw" && request.body.raw === graphqlText(content.body.graphql?.query ?? "", content.body.graphql?.variables ?? "");
|
|
10787
|
+
const body3 = untouched ? content.body : request.body.mode === "raw" ? {
|
|
10788
|
+
...content.body,
|
|
10789
|
+
mode: "raw",
|
|
10790
|
+
raw: {
|
|
10791
|
+
language: content.body.mode === "graphql" ? "json" : content.body.raw?.language ?? "text",
|
|
10792
|
+
text: request.body.raw
|
|
10793
|
+
}
|
|
10794
|
+
} : request.body.mode === "urlencoded" ? { ...content.body, mode: "urlencoded", urlencoded: rows2(request.body.urlencoded) } : content.body;
|
|
10795
|
+
return { ...content, method: request.method, url: request.url, headers: rows2(request.headers), body: body3 };
|
|
10796
|
+
}
|
|
10797
|
+
function sentScriptRequest(content, sent) {
|
|
10798
|
+
const { mode } = content.body;
|
|
10799
|
+
const body3 = mode === "multipart" || mode === "binary" ? { mode } : sent.body === void 0 ? { mode: "none" } : mode === "urlencoded" ? { mode, urlencoded: [...new URLSearchParams(sent.body)] } : { mode: "raw", raw: sent.body };
|
|
10800
|
+
return { method: sent.method, url: sent.url, headers: sent.headers, body: body3 };
|
|
10801
|
+
}
|
|
10802
|
+
|
|
10844
10803
|
// ../core/src/send/assertions.ts
|
|
10845
10804
|
function runAssertions(assertions, response) {
|
|
10846
10805
|
return assertions.map((assertion) => evaluate2(assertion, response));
|
|
@@ -11017,7 +10976,7 @@ function sentBodyPreview(body3) {
|
|
|
11017
10976
|
return `@file ${file(body3)}`;
|
|
11018
10977
|
}
|
|
11019
10978
|
function scriptResponse(response) {
|
|
11020
|
-
const body3 = bodyText(response.body);
|
|
10979
|
+
const body3 = response.text ?? bodyText(response.body);
|
|
11021
10980
|
return {
|
|
11022
10981
|
code: response.statusCode,
|
|
11023
10982
|
status: response.statusText,
|
|
@@ -11097,6 +11056,7 @@ async function exchange(spec, ports) {
|
|
|
11097
11056
|
scriptExtras(ports, spec, "prerequest", scriptRequestOf(template))
|
|
11098
11057
|
);
|
|
11099
11058
|
timings.preScriptMs += Math.round(performance.now() - scriptStarted);
|
|
11059
|
+
stopped(spec);
|
|
11100
11060
|
preRuns.push({ name, result });
|
|
11101
11061
|
const { changes } = result;
|
|
11102
11062
|
restoreSecrets.push(await applyChanges(ports, spec, changes, notes));
|
|
@@ -11121,6 +11081,7 @@ async function exchange(spec, ports) {
|
|
|
11121
11081
|
);
|
|
11122
11082
|
}
|
|
11123
11083
|
}
|
|
11084
|
+
stopped(spec);
|
|
11124
11085
|
let request;
|
|
11125
11086
|
try {
|
|
11126
11087
|
const built2 = spec.buildRequest(template);
|
|
@@ -11154,6 +11115,7 @@ async function exchange(spec, ports) {
|
|
|
11154
11115
|
};
|
|
11155
11116
|
const transmit = async () => {
|
|
11156
11117
|
startedAt = performance.now();
|
|
11118
|
+
delete policy.startedAt;
|
|
11157
11119
|
sentCookies = void 0;
|
|
11158
11120
|
try {
|
|
11159
11121
|
return await ports.transport.send(
|
|
@@ -11189,7 +11151,7 @@ async function exchange(spec, ports) {
|
|
|
11189
11151
|
outcome = await transmit();
|
|
11190
11152
|
}
|
|
11191
11153
|
}
|
|
11192
|
-
const durationMs = Math.round(performance.now() - startedAt);
|
|
11154
|
+
const durationMs = Math.round(performance.now() - (policy.startedAt ?? startedAt));
|
|
11193
11155
|
timings.requestMs = durationMs;
|
|
11194
11156
|
ports.watch?.received(request.url);
|
|
11195
11157
|
const shaped = outcome instanceof TransportError ? shapeFailure(outcome, ports.page, ports.online?.()) : shapeResponse(outcome, ports.page);
|
|
@@ -11214,6 +11176,7 @@ async function exchange(spec, ports) {
|
|
|
11214
11176
|
statusCode: shaped.response.statusCode ?? 0,
|
|
11215
11177
|
statusText: shaped.response.statusText ?? "",
|
|
11216
11178
|
body: shaped.body,
|
|
11179
|
+
...shaped.text !== void 0 && { text: shaped.text },
|
|
11217
11180
|
headers: shaped.headers,
|
|
11218
11181
|
durationMs,
|
|
11219
11182
|
sizeBytes: outcome instanceof TransportError ? void 0 : outcome.sizes.decoded
|
|
@@ -11284,6 +11247,9 @@ async function exchange(spec, ports) {
|
|
|
11284
11247
|
...nextRequest !== void 0 && { nextRequest }
|
|
11285
11248
|
};
|
|
11286
11249
|
}
|
|
11250
|
+
function stopped(spec) {
|
|
11251
|
+
if (spec.signal?.aborted) throw new SendBlockedError("You stopped the request. Nothing was sent.");
|
|
11252
|
+
}
|
|
11287
11253
|
function firstSentence(text3) {
|
|
11288
11254
|
const match = /^.*?[.!?](?=\s|$)/s.exec(text3.trim());
|
|
11289
11255
|
return (match ? match[0] : text3).trim();
|
|
@@ -11319,7 +11285,7 @@ function recordOf(history, raw) {
|
|
|
11319
11285
|
};
|
|
11320
11286
|
}
|
|
11321
11287
|
function shapeResponse(envelope, page) {
|
|
11322
|
-
const body3 =
|
|
11288
|
+
const { value: body3, text: sentText } = readEnvelope(envelope);
|
|
11323
11289
|
const isError = envelope.status < 200 || envelope.status >= 300;
|
|
11324
11290
|
const shown = body3;
|
|
11325
11291
|
const bodyIsJson = isJsonPayload(shown);
|
|
@@ -11328,6 +11294,7 @@ function shapeResponse(envelope, page) {
|
|
|
11328
11294
|
const contentLength = Number(header("content-length") || NaN);
|
|
11329
11295
|
return {
|
|
11330
11296
|
body: body3,
|
|
11297
|
+
...sentText !== void 0 && { text: sentText },
|
|
11331
11298
|
headers: Object.fromEntries(envelope.headers),
|
|
11332
11299
|
headerList: envelope.headers,
|
|
11333
11300
|
bodyText: body3 === void 0 || body3 === null || body3 instanceof BinaryBody ? void 0 : text3,
|
|
@@ -11423,18 +11390,25 @@ async function sendForScript(ports, request, signal, route = ports.transport.rou
|
|
|
11423
11390
|
);
|
|
11424
11391
|
}
|
|
11425
11392
|
const startedAt = performance.now();
|
|
11393
|
+
const policy = { route };
|
|
11426
11394
|
const envelope = await ports.transport.send(
|
|
11427
11395
|
request,
|
|
11428
|
-
{
|
|
11429
|
-
|
|
11396
|
+
{
|
|
11397
|
+
signal: signal ?? new AbortController().signal,
|
|
11398
|
+
timeoutMs: ports.timeoutMs(),
|
|
11399
|
+
// Followed on every route, as a browser follows it: the vault is not read for this request, so no secret is held to its host.
|
|
11400
|
+
redirects: { follow: true, stayOnHost: false }
|
|
11401
|
+
},
|
|
11402
|
+
policy
|
|
11430
11403
|
);
|
|
11431
11404
|
const shaped = shapeResponse(envelope, ports.page);
|
|
11432
11405
|
return scriptResponse({
|
|
11433
11406
|
statusCode: envelope.status,
|
|
11434
11407
|
statusText: envelope.statusText,
|
|
11435
11408
|
body: shaped.body,
|
|
11409
|
+
...shaped.text !== void 0 && { text: shaped.text },
|
|
11436
11410
|
headers: shaped.headers,
|
|
11437
|
-
durationMs: Math.round(performance.now() - startedAt),
|
|
11411
|
+
durationMs: Math.round(performance.now() - (policy.startedAt ?? startedAt)),
|
|
11438
11412
|
sizeBytes: envelope.sizes.decoded
|
|
11439
11413
|
});
|
|
11440
11414
|
}
|
|
@@ -11521,6 +11495,88 @@ function stringifyPayload(payload) {
|
|
|
11521
11495
|
return stringifyJson(payload, 4) ?? String(payload);
|
|
11522
11496
|
}
|
|
11523
11497
|
|
|
11498
|
+
// ../core/src/auth/sigv4.ts
|
|
11499
|
+
var utf83 = (text3) => new TextEncoder().encode(text3);
|
|
11500
|
+
var sha2562 = async (data) => new Uint8Array(await crypto.subtle.digest("SHA-256", data));
|
|
11501
|
+
var hmac2 = async (key, data) => new Uint8Array(
|
|
11502
|
+
await crypto.subtle.sign(
|
|
11503
|
+
"HMAC",
|
|
11504
|
+
await crypto.subtle.importKey("raw", key, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]),
|
|
11505
|
+
utf83(data)
|
|
11506
|
+
)
|
|
11507
|
+
);
|
|
11508
|
+
var hex = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
11509
|
+
var encode = (text3) => encodeURIComponent(text3).replace(/[!'()*]/g, (mark) => `%${mark.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
11510
|
+
var decode = (text3) => {
|
|
11511
|
+
try {
|
|
11512
|
+
return decodeURIComponent(text3);
|
|
11513
|
+
} catch (error) {
|
|
11514
|
+
if (!(error instanceof URIError)) throw error;
|
|
11515
|
+
return text3;
|
|
11516
|
+
}
|
|
11517
|
+
};
|
|
11518
|
+
async function signV4(request, credentials, at) {
|
|
11519
|
+
const url = new URL(request.url);
|
|
11520
|
+
const s3 = credentials.service === "s3";
|
|
11521
|
+
const segments = url.pathname.split("/");
|
|
11522
|
+
const kept = segments.filter(Boolean);
|
|
11523
|
+
const path = s3 ? segments.map((segment) => encode(decode(segment))).join("/") : `/${kept.map(encode).join("/")}${kept.length && url.pathname.endsWith("/") ? "/" : ""}`;
|
|
11524
|
+
const query = url.search.slice(1).split("&").filter(Boolean).map((pair) => {
|
|
11525
|
+
const cut = pair.indexOf("=");
|
|
11526
|
+
return [
|
|
11527
|
+
encode(decode(cut === -1 ? pair : pair.slice(0, cut))),
|
|
11528
|
+
encode(decode(cut === -1 ? "" : pair.slice(cut + 1)))
|
|
11529
|
+
];
|
|
11530
|
+
}).sort(
|
|
11531
|
+
([keyA, valueA], [keyB, valueB]) => keyA === keyB ? valueA < valueB ? -1 : valueA > valueB ? 1 : 0 : keyA < keyB ? -1 : 1
|
|
11532
|
+
).map(([key2, value]) => `${key2}=${value}`).join("&");
|
|
11533
|
+
const date = at.toISOString().replace(/[-:]|\.\d{3}/g, "");
|
|
11534
|
+
const day = date.slice(0, 8);
|
|
11535
|
+
const body3 = typeof request.body === "string" ? utf83(request.body) : new Uint8Array(request.body ?? []);
|
|
11536
|
+
const bodyHash = hex(await sha2562(body3));
|
|
11537
|
+
const added2 = [
|
|
11538
|
+
["X-Amz-Date", date],
|
|
11539
|
+
...credentials.sessionToken ? [["X-Amz-Security-Token", credentials.sessionToken]] : [],
|
|
11540
|
+
...s3 ? [["X-Amz-Content-Sha256", bodyHash]] : []
|
|
11541
|
+
];
|
|
11542
|
+
const byName = /* @__PURE__ */ new Map();
|
|
11543
|
+
const own3 = request.headers.filter(
|
|
11544
|
+
([name]) => !added2.some(([written]) => written.toLowerCase() === name.toLowerCase()) && name.toLowerCase() !== "host"
|
|
11545
|
+
);
|
|
11546
|
+
for (const [name, value] of [["Host", url.host], ...own3, ...added2]) {
|
|
11547
|
+
const key2 = name.toLowerCase();
|
|
11548
|
+
byName.set(key2, [...byName.get(key2) ?? [], value.trim().replace(/\s+/g, " ")]);
|
|
11549
|
+
}
|
|
11550
|
+
const names = [...byName.keys()].sort();
|
|
11551
|
+
const signedHeaders = names.join(";");
|
|
11552
|
+
const canonicalRequest = [
|
|
11553
|
+
request.method,
|
|
11554
|
+
path,
|
|
11555
|
+
query,
|
|
11556
|
+
...names.map((name) => `${name}:${(byName.get(name) ?? []).join(",")}`),
|
|
11557
|
+
"",
|
|
11558
|
+
signedHeaders,
|
|
11559
|
+
bodyHash
|
|
11560
|
+
].join("\n");
|
|
11561
|
+
const scope = `${day}/${credentials.region}/${credentials.service}/aws4_request`;
|
|
11562
|
+
const stringToSign = ["AWS4-HMAC-SHA256", date, scope, hex(await sha2562(utf83(canonicalRequest)))].join("\n");
|
|
11563
|
+
let key = utf83(`AWS4${credentials.secretKey}`);
|
|
11564
|
+
for (const part of [day, credentials.region, credentials.service, "aws4_request"]) key = await hmac2(key, part);
|
|
11565
|
+
const signature = hex(await hmac2(key, stringToSign));
|
|
11566
|
+
return {
|
|
11567
|
+
headers: [
|
|
11568
|
+
...added2,
|
|
11569
|
+
[
|
|
11570
|
+
"Authorization",
|
|
11571
|
+
`AWS4-HMAC-SHA256 Credential=${credentials.accessKey}/${scope}, SignedHeaders=${signedHeaders}, Signature=${signature}`
|
|
11572
|
+
]
|
|
11573
|
+
],
|
|
11574
|
+
canonicalRequest,
|
|
11575
|
+
stringToSign,
|
|
11576
|
+
signature
|
|
11577
|
+
};
|
|
11578
|
+
}
|
|
11579
|
+
|
|
11524
11580
|
// ../core/src/send/build.ts
|
|
11525
11581
|
var UnresolvedVariablesError = class extends SendBlockedError {
|
|
11526
11582
|
constructor(names) {
|
|
@@ -11544,6 +11600,14 @@ function normalizeUrl(text3) {
|
|
|
11544
11600
|
const address2 = text3.replace(/[\t\n\r]/g, encodeURIComponent);
|
|
11545
11601
|
return hasExplicitScheme(address2) ? address2 : `https://${address2}`;
|
|
11546
11602
|
}
|
|
11603
|
+
function validateUrl(text3) {
|
|
11604
|
+
if (!text3) return false;
|
|
11605
|
+
const parsed2 = URL.parse(normalizeUrl(text3));
|
|
11606
|
+
if (!parsed2 || parsed2.protocol !== "http:" && parsed2.protocol !== "https:" || !parsed2.hostname) return false;
|
|
11607
|
+
if (hasExplicitScheme(text3)) return true;
|
|
11608
|
+
const { hostname } = parsed2;
|
|
11609
|
+
return hostname === "localhost" || hostname.includes(".") || hostname.includes(":") || !!parsed2.port;
|
|
11610
|
+
}
|
|
11547
11611
|
function appendQueryParam(url, key, value) {
|
|
11548
11612
|
const parsed2 = URL.parse(normalizeUrl(url));
|
|
11549
11613
|
if (!parsed2) {
|
|
@@ -11598,12 +11662,18 @@ function buildRequest(content, context) {
|
|
|
11598
11662
|
if (resolver.unresolved.size && context.blockUnresolved) {
|
|
11599
11663
|
throw new UnresolvedVariablesError([...resolver.unresolved]);
|
|
11600
11664
|
}
|
|
11665
|
+
if (!validateUrl(resolver.resolve(content.url.trim()))) {
|
|
11666
|
+
throw new SendBlockedError(
|
|
11667
|
+
"This is not an address a request can go to. Write it as https://host/path. Nothing was sent."
|
|
11668
|
+
);
|
|
11669
|
+
}
|
|
11601
11670
|
return request;
|
|
11602
11671
|
};
|
|
11603
11672
|
return built2 instanceof Promise ? built2.then(check) : check(built2);
|
|
11604
11673
|
};
|
|
11605
|
-
|
|
11606
|
-
|
|
11674
|
+
const oauth = sentAuth(content.auth, context.ancestors).type === "oauth2";
|
|
11675
|
+
if (!probe.lockedSecrets.size && !oauth) return checked(new VariableResolver(scopes()));
|
|
11676
|
+
const withSecrets = () => probe.lockedSecrets.size ? context.readSecrets([...probe.lockedSecrets]).then((secrets) => checked(new VariableResolver(scopes(), (id) => secrets.get(id)))) : checked(new VariableResolver(scopes()));
|
|
11607
11677
|
const from = composed && hostOf(composed.url, composed.scopes);
|
|
11608
11678
|
const to = hostOf(endpointText, scopes());
|
|
11609
11679
|
if (from === void 0 || from === to || composed?.accepted?.has(to)) return withSecrets();
|
|
@@ -12105,6 +12175,7 @@ var VALUE_OPTIONS = {
|
|
|
12105
12175
|
"--data-raw": "data-raw",
|
|
12106
12176
|
"--data-binary": "data-binary",
|
|
12107
12177
|
"--data-urlencode": "data-urlencode",
|
|
12178
|
+
"--json": "json",
|
|
12108
12179
|
"-F": "form",
|
|
12109
12180
|
"--form": "form",
|
|
12110
12181
|
"--form-string": "form-string",
|
|
@@ -12210,7 +12281,16 @@ var SKIPPED_VALUE_OPTIONS = /* @__PURE__ */ new Set([
|
|
|
12210
12281
|
"--trace-ascii",
|
|
12211
12282
|
"--stderr",
|
|
12212
12283
|
"--proto",
|
|
12213
|
-
"--proto-default"
|
|
12284
|
+
"--proto-default",
|
|
12285
|
+
"--url-query",
|
|
12286
|
+
"--proxy-header",
|
|
12287
|
+
"--socks5",
|
|
12288
|
+
"--socks5-hostname",
|
|
12289
|
+
"--preproxy",
|
|
12290
|
+
"--request-target",
|
|
12291
|
+
"--doh-url",
|
|
12292
|
+
"--output-dir",
|
|
12293
|
+
"--netrc-file"
|
|
12214
12294
|
]);
|
|
12215
12295
|
var formEncode = (text3) => new URLSearchParams([[text3, ""]]).toString().slice(0, -1);
|
|
12216
12296
|
var contentType = (headers2) => headers2.filter(([name]) => name.toLowerCase() === "content-type").at(-1)?.[1];
|
|
@@ -12252,7 +12332,7 @@ function parseCurl(text3) {
|
|
|
12252
12332
|
const urls = [];
|
|
12253
12333
|
let method;
|
|
12254
12334
|
let basic;
|
|
12255
|
-
let [get, head, binaryOnly] = [false, false, true];
|
|
12335
|
+
let [get, head, binaryOnly, json] = [false, false, true, false];
|
|
12256
12336
|
const take = (kind, value) => {
|
|
12257
12337
|
switch (kind) {
|
|
12258
12338
|
case "method":
|
|
@@ -12267,7 +12347,9 @@ function parseCurl(text3) {
|
|
|
12267
12347
|
}
|
|
12268
12348
|
case "data":
|
|
12269
12349
|
case "data-binary":
|
|
12270
|
-
|
|
12350
|
+
case "json":
|
|
12351
|
+
if (kind === "json") json = true;
|
|
12352
|
+
if (kind !== "data-binary") binaryOnly = false;
|
|
12271
12353
|
data.push(value.startsWith("@") ? { file: value.slice(1) } : { text: value });
|
|
12272
12354
|
break;
|
|
12273
12355
|
case "data-raw":
|
|
@@ -12346,6 +12428,11 @@ function parseCurl(text3) {
|
|
|
12346
12428
|
`The command names ${urls.length} addresses. The first is used; the others were left out: ${urls.slice(1).join(", ")}.`
|
|
12347
12429
|
);
|
|
12348
12430
|
let url = urls[0];
|
|
12431
|
+
if (json) {
|
|
12432
|
+
for (const name of ["Content-Type", "Accept"])
|
|
12433
|
+
if (!headers2.some(([named2]) => named2.toLowerCase() === name.toLowerCase()))
|
|
12434
|
+
headers2.push([name, "application/json"]);
|
|
12435
|
+
}
|
|
12349
12436
|
const type = (contentType(headers2) ?? "").toLowerCase();
|
|
12350
12437
|
let body3 = { mode: "none" };
|
|
12351
12438
|
const files = data.flatMap((datum) => "file" in datum ? [datum.file] : []);
|
|
@@ -12362,7 +12449,9 @@ function parseCurl(text3) {
|
|
|
12362
12449
|
} else if (data.length) {
|
|
12363
12450
|
if (files.length) warnings.push(`The data of the file ${files.join(", ")} was left out: the file was not read.`);
|
|
12364
12451
|
const wire = data.flatMap((datum) => "text" in datum ? [datum.text] : []).join("&");
|
|
12365
|
-
|
|
12452
|
+
const pairs2 = wire.split("&").every((piece) => /^[\w.~%+\-[\]]*(=|$)/.test(piece));
|
|
12453
|
+
if (!type && !pairs2) headers2.push(["Content-Type", "application/x-www-form-urlencoded"]);
|
|
12454
|
+
body3 = !type && pairs2 || type.startsWith("application/x-www-form-urlencoded") ? { mode: "urlencoded", fields: [...new URLSearchParams(wire)] } : { mode: "raw", text: wire };
|
|
12366
12455
|
}
|
|
12367
12456
|
method ??= head ? "HEAD" : body3.mode === "none" ? "GET" : "POST";
|
|
12368
12457
|
if (!isHttpMethod(method))
|
|
@@ -13197,7 +13286,9 @@ function format4(value) {
|
|
|
13197
13286
|
const children = /* @__PURE__ */ new Map();
|
|
13198
13287
|
for (const resource of resources) {
|
|
13199
13288
|
const parent = text2(field3(resource, "parentId"));
|
|
13200
|
-
|
|
13289
|
+
const siblings = children.get(parent);
|
|
13290
|
+
if (siblings) siblings.push(resource);
|
|
13291
|
+
else children.set(parent, [resource]);
|
|
13201
13292
|
}
|
|
13202
13293
|
for (const list4 of children.values()) list4.sort((a, b) => sortKey(a) - sortKey(b));
|
|
13203
13294
|
const under = (parent, type) => parent === "" ? [] : (children.get(parent) ?? []).filter((resource) => kind(resource) === type);
|
|
@@ -13667,13 +13758,17 @@ var NOT_READABLE = "The file is neither JSON nor YAML that can be read, so it is
|
|
|
13667
13758
|
function parseYaml(text3) {
|
|
13668
13759
|
if (text3.length > MAX_YAML_CHARS) throw new ImportError(YAML_TOO_LARGE);
|
|
13669
13760
|
try {
|
|
13670
|
-
return
|
|
13671
|
-
|
|
13672
|
-
|
|
13673
|
-
|
|
13674
|
-
|
|
13675
|
-
|
|
13676
|
-
|
|
13761
|
+
return JSON.parse(
|
|
13762
|
+
JSON.stringify(
|
|
13763
|
+
(0, import_yaml.parse)(text3, {
|
|
13764
|
+
maxAliasCount: MAX_YAML_ALIASES,
|
|
13765
|
+
schema: "core",
|
|
13766
|
+
logLevel: "error",
|
|
13767
|
+
uniqueKeys: false,
|
|
13768
|
+
prettyErrors: false
|
|
13769
|
+
})
|
|
13770
|
+
)
|
|
13771
|
+
);
|
|
13677
13772
|
} catch (error) {
|
|
13678
13773
|
throw new ImportError(error instanceof Error && /alias count/i.test(error.message) ? YAML_ALIASES : NOT_READABLE);
|
|
13679
13774
|
}
|
|
@@ -13782,7 +13877,13 @@ function added(payload) {
|
|
|
13782
13877
|
return { payload: checked, plan };
|
|
13783
13878
|
}
|
|
13784
13879
|
function importText(text3, options = {}) {
|
|
13785
|
-
|
|
13880
|
+
let imported;
|
|
13881
|
+
try {
|
|
13882
|
+
imported = readText(text3, options);
|
|
13883
|
+
} catch (error) {
|
|
13884
|
+
if (error instanceof RangeError) throw new ImportError("The file is nested too deeply to read.");
|
|
13885
|
+
throw error;
|
|
13886
|
+
}
|
|
13786
13887
|
return {
|
|
13787
13888
|
...imported,
|
|
13788
13889
|
report: {
|
|
@@ -14053,7 +14154,7 @@ function runReportHtml(report, info) {
|
|
|
14053
14154
|
:root { color-scheme: light dark; ${palette(REPORT_COLOURS.light)} }
|
|
14054
14155
|
@media (prefers-color-scheme: dark) { :root { ${palette(REPORT_COLOURS.dark)} } }
|
|
14055
14156
|
body { margin: 0; padding: 24px 16px; background: var(--canvas); color: var(--primary);
|
|
14056
|
-
font: 14px/1.5
|
|
14157
|
+
font: 14px/1.5 Figtree, system-ui, sans-serif; }
|
|
14057
14158
|
main { max-width: 1100px; margin: 0 auto; }
|
|
14058
14159
|
h1 { margin: 0 0 4px; font-size: 1.375rem; font-weight: 600; }
|
|
14059
14160
|
.meta, .counts { margin: 0 0 16px; color: var(--secondary); }
|
|
@@ -14262,6 +14363,8 @@ Options:
|
|
|
14262
14363
|
-e, --environment <file> The variables: a Wayfarer environments file or a
|
|
14263
14364
|
Postman environment. A workspace backup brings
|
|
14264
14365
|
its active environment unless one is given.
|
|
14366
|
+
--environment-name <name>
|
|
14367
|
+
Which environment of a file that holds several.
|
|
14265
14368
|
-d, --data <file> A data file, CSV or JSON: one iteration per row.
|
|
14266
14369
|
-n, --iterations <n> How many iterations (default: one per row of the
|
|
14267
14370
|
data file, or 1).
|
|
@@ -14272,8 +14375,9 @@ Options:
|
|
|
14272
14375
|
in --out.
|
|
14273
14376
|
--out <dir> Where the reports are written (default: here).
|
|
14274
14377
|
--bail Stop at the first failure.
|
|
14275
|
-
--timeout <ms> Give up on a request after this long
|
|
14276
|
-
|
|
14378
|
+
--timeout <ms> Give up on a request after this long, at most
|
|
14379
|
+
300000 (default 300000: Node's fetch waits no
|
|
14380
|
+
longer for a response).
|
|
14277
14381
|
--insecure Do not verify servers' TLS certificates.
|
|
14278
14382
|
--allow-scripts Run the file's scripts. A file's scripts are not
|
|
14279
14383
|
reviewed, so a collection that has any is not run
|
|
@@ -14284,8 +14388,10 @@ Options:
|
|
|
14284
14388
|
-v, --version
|
|
14285
14389
|
-h, --help
|
|
14286
14390
|
|
|
14287
|
-
Exit codes: 0 every request answered and every test passed; 1 a test failed
|
|
14288
|
-
|
|
14391
|
+
Exit codes: 0 every request answered and every test passed; 1 a test failed,
|
|
14392
|
+
a request got no response, or a report could not be written; 2 nothing was
|
|
14393
|
+
run (see the message).`;
|
|
14394
|
+
var NODE_FETCH_TIMEOUT_MS = 3e5;
|
|
14289
14395
|
var Refusal = class extends Error {
|
|
14290
14396
|
};
|
|
14291
14397
|
async function readText2(file, what) {
|
|
@@ -14297,7 +14403,7 @@ async function readText2(file, what) {
|
|
|
14297
14403
|
);
|
|
14298
14404
|
}
|
|
14299
14405
|
}
|
|
14300
|
-
function pick(list4, name, what, file) {
|
|
14406
|
+
function pick(list4, name, what, flag2, file) {
|
|
14301
14407
|
if (name !== void 0) {
|
|
14302
14408
|
const found = list4.find((entry) => entry.name === name);
|
|
14303
14409
|
if (!found)
|
|
@@ -14308,7 +14414,7 @@ function pick(list4, name, what, file) {
|
|
|
14308
14414
|
}
|
|
14309
14415
|
if (list4.length > 1)
|
|
14310
14416
|
throw new Refusal(
|
|
14311
|
-
`${file} holds ${list4.length} ${what}s: name one with --${
|
|
14417
|
+
`${file} holds ${list4.length} ${what}s: name one with --${flag2} (${list4.map((entry) => `"${entry.name}"`).join(", ")}).`
|
|
14312
14418
|
);
|
|
14313
14419
|
const [only] = list4;
|
|
14314
14420
|
if (!only) throw new Refusal(`${file} holds no ${what}.`);
|
|
@@ -14334,6 +14440,7 @@ async function readSource(file, collection) {
|
|
|
14334
14440
|
collections.map(({ payload }) => ({ name: payload.collection.name, payload })),
|
|
14335
14441
|
collection,
|
|
14336
14442
|
"collection",
|
|
14443
|
+
"collection",
|
|
14337
14444
|
file
|
|
14338
14445
|
).payload,
|
|
14339
14446
|
globals: []
|
|
@@ -14348,7 +14455,7 @@ async function readSource(file, collection) {
|
|
|
14348
14455
|
throw new Refusal(`${file} is not a workspace backup this version reads:
|
|
14349
14456
|
${errors.join("\n ")}`);
|
|
14350
14457
|
const { stores } = parsed2.ok ? parsed2.value : {};
|
|
14351
|
-
const chosen = pick(stores.collections, collection, "collection", file);
|
|
14458
|
+
const chosen = pick(stores.collections, collection, "collection", "collection", file);
|
|
14352
14459
|
const source = {
|
|
14353
14460
|
$id: "wayfarer/collection",
|
|
14354
14461
|
meta: chosen.meta,
|
|
@@ -14370,11 +14477,11 @@ async function readSource(file, collection) {
|
|
|
14370
14477
|
}
|
|
14371
14478
|
};
|
|
14372
14479
|
}
|
|
14373
|
-
async function readEnvironment(file) {
|
|
14480
|
+
async function readEnvironment(file, name) {
|
|
14374
14481
|
try {
|
|
14375
14482
|
const { environments } = importText(await readText2(file, "environment"));
|
|
14376
|
-
const
|
|
14377
|
-
return { name, vars };
|
|
14483
|
+
const chosen = pick(environments, name, "environment", "environment-name", file);
|
|
14484
|
+
return { name: chosen.name, vars: chosen.vars };
|
|
14378
14485
|
} catch (error) {
|
|
14379
14486
|
if (error instanceof Error && error.name === "ImportError") throw new Refusal(`${file}: ${error.message}`);
|
|
14380
14487
|
throw error;
|
|
@@ -14420,6 +14527,7 @@ async function main(argv) {
|
|
|
14420
14527
|
allowPositionals: true,
|
|
14421
14528
|
options: {
|
|
14422
14529
|
environment: { type: "string", short: "e" },
|
|
14530
|
+
"environment-name": { type: "string" },
|
|
14423
14531
|
data: { type: "string", short: "d" },
|
|
14424
14532
|
iterations: { type: "string", short: "n" },
|
|
14425
14533
|
collection: { type: "string" },
|
|
@@ -14435,7 +14543,7 @@ async function main(argv) {
|
|
|
14435
14543
|
}
|
|
14436
14544
|
});
|
|
14437
14545
|
if (values.version) {
|
|
14438
|
-
console.log("2.
|
|
14546
|
+
console.log("2.2.0");
|
|
14439
14547
|
return 0;
|
|
14440
14548
|
}
|
|
14441
14549
|
const [command, file, ...rest] = positionals;
|
|
@@ -14459,6 +14567,8 @@ ${USAGE}`);
|
|
|
14459
14567
|
const iterations = number(values.iterations, "iterations");
|
|
14460
14568
|
if (iterations === 0) throw new Refusal("--iterations takes 1 or more.");
|
|
14461
14569
|
const timeoutMs = number(values.timeout, "timeout") ?? 0;
|
|
14570
|
+
if (timeoutMs > NODE_FETCH_TIMEOUT_MS)
|
|
14571
|
+
throw new Refusal(`--timeout takes at most ${NODE_FETCH_TIMEOUT_MS}: Node's fetch waits no longer for a response.`);
|
|
14462
14572
|
const read = await readSource(file, values.collection);
|
|
14463
14573
|
const holders = scriptHolders(read.source);
|
|
14464
14574
|
if (holders.length && !values["allow-scripts"]) {
|
|
@@ -14466,7 +14576,9 @@ ${USAGE}`);
|
|
|
14466
14576
|
`This collection has scripts, in ${holders.join(", ")}. A file's scripts have not been reviewed: read them, and run again with --allow-scripts only if you trust them. Nothing was sent.`
|
|
14467
14577
|
);
|
|
14468
14578
|
}
|
|
14469
|
-
|
|
14579
|
+
if (values["environment-name"] !== void 0 && values.environment === void 0)
|
|
14580
|
+
throw new Refusal("--environment-name names an environment of the file given with --environment.");
|
|
14581
|
+
const environment2 = values.environment === void 0 ? read.environment : await readEnvironment(values.environment, values["environment-name"]);
|
|
14470
14582
|
const vault = values["vault-file"] === void 0 ? read.vault : await readVaultFile(values["vault-file"]);
|
|
14471
14583
|
let data;
|
|
14472
14584
|
if (values.data !== void 0) {
|
|
@@ -14507,12 +14619,21 @@ ${USAGE}`);
|
|
|
14507
14619
|
const info = { name: read.source.collection.name, startedAt };
|
|
14508
14620
|
if (reporters.has("cli")) console.log(`
|
|
14509
14621
|
${summary(report)}`);
|
|
14510
|
-
|
|
14511
|
-
|
|
14512
|
-
|
|
14513
|
-
|
|
14514
|
-
|
|
14515
|
-
|
|
14622
|
+
const code = report.results.some(runFailed) ? 1 : 0;
|
|
14623
|
+
try {
|
|
14624
|
+
if (reporters.has("json"))
|
|
14625
|
+
await writeFile(
|
|
14626
|
+
join(values.out, "wayfarer-run.json"),
|
|
14627
|
+
`${JSON.stringify(runReportJson(report, info), null, 2)}
|
|
14628
|
+
`
|
|
14629
|
+
);
|
|
14630
|
+
if (reporters.has("junit")) await writeFile(join(values.out, "wayfarer-run.junit.xml"), junitXml(report, info));
|
|
14631
|
+
if (reporters.has("html")) await writeFile(join(values.out, "wayfarer-run.html"), runReportHtml(report, info));
|
|
14632
|
+
} catch (error) {
|
|
14633
|
+
console.error(`--out ${values.out}: ${error instanceof Error ? error.message : String(error)}`);
|
|
14634
|
+
return 1;
|
|
14635
|
+
}
|
|
14636
|
+
return code;
|
|
14516
14637
|
}
|
|
14517
14638
|
main(process.argv.slice(2)).then(
|
|
14518
14639
|
(code) => process.exitCode = code,
|