apitestcenter 0.7.0 → 0.9.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/dist/THIRD-PARTY-NOTICES-UI.txt +1 -1
- package/dist/THIRD-PARTY-NOTICES.txt +1 -1
- package/dist/cli.mjs +1064 -364
- package/dist/ui/404/index.html +1 -1
- package/dist/ui/404.html +1 -1
- package/dist/ui/__next.__PAGE__.txt +10 -10
- package/dist/ui/__next._full.txt +11 -11
- package/dist/ui/__next._tree.txt +2 -2
- package/dist/ui/_next/static/chunks/{0txpfp028djv6.js → 13xdai3-mkg7c.js} +66 -65
- package/dist/ui/_next/static/chunks/2i7s5kuurlkr6.css +3 -0
- package/dist/ui/_next/static/chunks/2od90u8fd_sla.js +18 -0
- package/dist/ui/_next/static/chunks/36lx6-56-udwb.js +2 -0
- package/dist/ui/_next/static/chunks/3qxq1d9u_kohv.js +1 -0
- package/dist/ui/_not-found/__next._full.txt +10 -10
- package/dist/ui/_not-found/__next._not-found.__PAGE__.txt +9 -9
- package/dist/ui/_not-found/__next._tree.txt +2 -2
- package/dist/ui/_not-found/index.html +1 -1
- package/dist/ui/_not-found/index.txt +10 -10
- package/dist/ui/compare/__next._full.txt +11 -11
- package/dist/ui/compare/__next._tree.txt +2 -2
- package/dist/ui/compare/__next.compare.__PAGE__.txt +10 -10
- package/dist/ui/compare/index.html +1 -1
- package/dist/ui/compare/index.txt +11 -11
- package/dist/ui/history/__next._full.txt +12 -12
- package/dist/ui/history/__next._tree.txt +2 -2
- package/dist/ui/history/__next.history.__PAGE__.txt +11 -11
- package/dist/ui/history/index.html +1 -1
- package/dist/ui/history/index.txt +12 -12
- package/dist/ui/index.html +1 -1
- package/dist/ui/index.txt +11 -11
- package/dist/ui/preview/__next._full.txt +12 -12
- package/dist/ui/preview/__next._tree.txt +2 -2
- package/dist/ui/preview/__next.preview.__PAGE__.txt +11 -11
- package/dist/ui/preview/index.html +1 -1
- package/dist/ui/preview/index.txt +12 -12
- package/dist/ui/suite/__next._full.txt +11 -11
- package/dist/ui/suite/__next._tree.txt +2 -2
- package/dist/ui/suite/__next.suite.__PAGE__.txt +10 -10
- package/dist/ui/suite/index.html +1 -1
- package/dist/ui/suite/index.txt +11 -11
- package/dist/ui/tests/__next._full.txt +10 -10
- package/dist/ui/tests/__next._tree.txt +2 -2
- package/dist/ui/tests/__next.tests.__PAGE__.txt +9 -9
- package/dist/ui/tests/index.html +1 -1
- package/dist/ui/tests/index.txt +10 -10
- package/package.json +1 -1
- package/dist/ui/_next/static/chunks/0-898cb98enjv.css +0 -3
- package/dist/ui/_next/static/chunks/0kfr8kv4cj_kv.js +0 -1
- package/dist/ui/_next/static/chunks/0vkcbl8nakdh1.js +0 -2
- package/dist/ui/_next/static/chunks/1w_q4qvvuinj2.js +0 -1
- /package/dist/ui/_next/static/{lBYayPKoT6YJhgfHFuw6O → C6zMFzDkVqF8IXny_v7JK}/_buildManifest.js +0 -0
- /package/dist/ui/_next/static/{lBYayPKoT6YJhgfHFuw6O → C6zMFzDkVqF8IXny_v7JK}/_clientMiddlewareManifest.js +0 -0
- /package/dist/ui/_next/static/{lBYayPKoT6YJhgfHFuw6O → C6zMFzDkVqF8IXny_v7JK}/_ssgManifest.js +0 -0
package/dist/cli.mjs
CHANGED
|
@@ -804,14 +804,41 @@ function safeSegment(text, fallback = "untitled") {
|
|
|
804
804
|
return s || fallback;
|
|
805
805
|
}
|
|
806
806
|
function defaultOAuth() {
|
|
807
|
-
return {
|
|
807
|
+
return {
|
|
808
|
+
grant: "client_credentials",
|
|
809
|
+
tokenUrl: "",
|
|
810
|
+
authUrl: "",
|
|
811
|
+
redirectUri: DEFAULT_REDIRECT_URI,
|
|
812
|
+
clientId: "",
|
|
813
|
+
clientSecret: "",
|
|
814
|
+
scope: "",
|
|
815
|
+
username: "",
|
|
816
|
+
password: "",
|
|
817
|
+
clientAuth: "basic",
|
|
818
|
+
pkce: true,
|
|
819
|
+
extra: [],
|
|
820
|
+
privateKey: "",
|
|
821
|
+
assertionAlgorithm: "RS256",
|
|
822
|
+
issuer: "",
|
|
823
|
+
subject: "",
|
|
824
|
+
audience: "",
|
|
825
|
+
kid: "",
|
|
826
|
+
certificate: "",
|
|
827
|
+
extraClaims: "",
|
|
828
|
+
samlSource: "idp",
|
|
829
|
+
idpUrl: "",
|
|
830
|
+
userId: "",
|
|
831
|
+
companyId: "",
|
|
832
|
+
assertion: ""
|
|
833
|
+
};
|
|
808
834
|
}
|
|
809
835
|
function oauthFromUnknown(value) {
|
|
810
836
|
const base = defaultOAuth();
|
|
811
837
|
if (!value || typeof value !== "object") return base;
|
|
812
838
|
const v = value;
|
|
813
839
|
const s = (x, d) => typeof x === "string" ? x : d;
|
|
814
|
-
const grants = ["client_credentials", "password", "authorization_code"];
|
|
840
|
+
const grants = ["client_credentials", "password", "authorization_code", "jwt_bearer", "saml_bearer"];
|
|
841
|
+
const algs = ["RS256", "RS384", "RS512", "PS256", "ES256", "ES384"];
|
|
815
842
|
return {
|
|
816
843
|
grant: grants.includes(v.grant) ? v.grant : base.grant,
|
|
817
844
|
tokenUrl: s(v.tokenUrl, ""),
|
|
@@ -822,8 +849,21 @@ function oauthFromUnknown(value) {
|
|
|
822
849
|
scope: s(v.scope, ""),
|
|
823
850
|
username: s(v.username, ""),
|
|
824
851
|
password: s(v.password, ""),
|
|
825
|
-
clientAuth: v.clientAuth === "body" ? "body" : "basic",
|
|
852
|
+
clientAuth: v.clientAuth === "body" ? "body" : v.clientAuth === "private_key_jwt" ? "private_key_jwt" : "basic",
|
|
826
853
|
pkce: v.pkce !== false,
|
|
854
|
+
privateKey: s(v.privateKey, ""),
|
|
855
|
+
assertionAlgorithm: algs.includes(v.assertionAlgorithm) ? v.assertionAlgorithm : "RS256",
|
|
856
|
+
issuer: s(v.issuer, ""),
|
|
857
|
+
subject: s(v.subject, ""),
|
|
858
|
+
audience: s(v.audience, ""),
|
|
859
|
+
kid: s(v.kid, ""),
|
|
860
|
+
certificate: s(v.certificate, ""),
|
|
861
|
+
extraClaims: s(v.extraClaims, ""),
|
|
862
|
+
samlSource: v.samlSource === "assertion" ? "assertion" : "idp",
|
|
863
|
+
idpUrl: s(v.idpUrl, ""),
|
|
864
|
+
userId: s(v.userId, ""),
|
|
865
|
+
companyId: s(v.companyId, ""),
|
|
866
|
+
assertion: s(v.assertion, ""),
|
|
827
867
|
extra: (Array.isArray(v.extra) ? v.extra : []).flatMap((e) => e && typeof e.key === "string" ? [{ key: e.key, value: s(e.value, "") }] : [])
|
|
828
868
|
};
|
|
829
869
|
}
|
|
@@ -858,9 +898,28 @@ function jwtFromUnknown(value) {
|
|
|
858
898
|
prefix: s(v.prefix, base.prefix)
|
|
859
899
|
};
|
|
860
900
|
}
|
|
901
|
+
function defaultOAuth1() {
|
|
902
|
+
return { consumerKey: "", consumerSecret: "", token: "", tokenSecret: "", signatureMethod: "HMAC-SHA256", realm: "", addTo: "header" };
|
|
903
|
+
}
|
|
904
|
+
function oauth1FromUnknown(value) {
|
|
905
|
+
const base = defaultOAuth1();
|
|
906
|
+
if (!value || typeof value !== "object") return base;
|
|
907
|
+
const v = value;
|
|
908
|
+
const s = (x, d) => typeof x === "string" ? x : d;
|
|
909
|
+
const methods = ["HMAC-SHA256", "HMAC-SHA1", "PLAINTEXT"];
|
|
910
|
+
return {
|
|
911
|
+
consumerKey: s(v.consumerKey, ""),
|
|
912
|
+
consumerSecret: s(v.consumerSecret, ""),
|
|
913
|
+
token: s(v.token, ""),
|
|
914
|
+
tokenSecret: s(v.tokenSecret, ""),
|
|
915
|
+
signatureMethod: methods.includes(v.signatureMethod) ? v.signatureMethod : base.signatureMethod,
|
|
916
|
+
realm: s(v.realm, ""),
|
|
917
|
+
addTo: v.addTo === "query" ? "query" : "header"
|
|
918
|
+
};
|
|
919
|
+
}
|
|
861
920
|
function emptyFolderSettings() {
|
|
862
921
|
return {
|
|
863
|
-
auth: { authMode: "inherit", oauth: defaultOAuth(), aws: defaultAws(), jwt: defaultJwt(), domain: "", token: "", username: "", password: "", apiName: "X-API-Key", apiValue: "", apiIn: "header" },
|
|
922
|
+
auth: { authMode: "inherit", oauth: defaultOAuth(), oauth1: defaultOAuth1(), aws: defaultAws(), jwt: defaultJwt(), domain: "", token: "", username: "", password: "", apiName: "X-API-Key", apiValue: "", apiIn: "header" },
|
|
864
923
|
headers: [],
|
|
865
924
|
variables: []
|
|
866
925
|
};
|
|
@@ -871,7 +930,7 @@ function folderFromUnknown(value) {
|
|
|
871
930
|
const v = value;
|
|
872
931
|
const a = v.auth ?? {};
|
|
873
932
|
const s = (x, d) => typeof x === "string" ? x : d;
|
|
874
|
-
const modes = ["inherit", "none", "bearer", "basic", "api_key", "oauth2", "digest", "ntlm", "aws_sigv4", "jwt"];
|
|
933
|
+
const modes = ["inherit", "none", "bearer", "basic", "api_key", "oauth2", "digest", "ntlm", "aws_sigv4", "jwt", "oauth1"];
|
|
875
934
|
const authMode2 = modes.includes(a.authMode) ? a.authMode : "inherit";
|
|
876
935
|
const headers = (Array.isArray(v.headers) ? v.headers : []).flatMap(
|
|
877
936
|
(h) => h && typeof h.key === "string" ? [{ key: h.key, value: s(h.value, "") }] : []
|
|
@@ -881,6 +940,7 @@ function folderFromUnknown(value) {
|
|
|
881
940
|
auth: {
|
|
882
941
|
authMode: authMode2,
|
|
883
942
|
oauth: oauthFromUnknown(a.oauth),
|
|
943
|
+
oauth1: oauth1FromUnknown(a.oauth1),
|
|
884
944
|
aws: awsFromUnknown(a.aws),
|
|
885
945
|
jwt: jwtFromUnknown(a.jwt),
|
|
886
946
|
domain: s(a.domain, ""),
|
|
@@ -917,7 +977,7 @@ var init_workspace = __esm({
|
|
|
917
977
|
FOLDER_FILE = "_folder.json";
|
|
918
978
|
MAX_FOLDER_DEPTH = 6;
|
|
919
979
|
DEFAULT_REDIRECT_URI = "http://127.0.0.1:53682/callback";
|
|
920
|
-
NESTED_SECRET_FIELDS = { oauth: ["clientSecret", "password"], aws: ["secretAccessKey", "sessionToken"], jwt: ["secret", "privateKey"] };
|
|
980
|
+
NESTED_SECRET_FIELDS = { oauth: ["clientSecret", "password", "privateKey", "assertion"], aws: ["secretAccessKey", "sessionToken"], jwt: ["secret", "privateKey"], oauth1: ["consumerSecret", "tokenSecret"] };
|
|
921
981
|
}
|
|
922
982
|
});
|
|
923
983
|
|
|
@@ -16010,7 +16070,7 @@ function checkLabel(check2) {
|
|
|
16010
16070
|
case "body":
|
|
16011
16071
|
return `body ${check2.op === "contains" ? "contains" : "does not contain"} "${clip(check2.value)}"`;
|
|
16012
16072
|
case "json":
|
|
16013
|
-
return check2.op === "exists" ? `${jsonPathLabel(check2.path)} exists` : `${jsonPathLabel(check2.path)} ${check2.op === "equals" ? "=" : check2.op === "not_equals" ? "\u2260" : "contains"} "${clip(check2.value)}"`;
|
|
16073
|
+
return check2.op === "exists" || check2.op === "not_exists" ? `${jsonPathLabel(check2.path)} ${check2.op === "exists" ? "exists" : "does not exist"}` : `${jsonPathLabel(check2.path)} ${check2.op === "equals" ? "=" : check2.op === "not_equals" ? "\u2260" : "contains"} "${clip(check2.value)}"`;
|
|
16014
16074
|
case "header":
|
|
16015
16075
|
return check2.op === "exists" ? `header ${check2.name} exists` : `header ${check2.name} ${check2.op === "equals" ? "=" : "contains"} "${clip(check2.value)}"`;
|
|
16016
16076
|
case "time":
|
|
@@ -16055,7 +16115,7 @@ var init_checks3 = __esm({
|
|
|
16055
16115
|
external_exports.object({
|
|
16056
16116
|
type: external_exports.literal("json"),
|
|
16057
16117
|
path: external_exports.string().min(1).max(300),
|
|
16058
|
-
op: external_exports.enum(["exists", "equals", "not_equals", "contains"]),
|
|
16118
|
+
op: external_exports.enum(["exists", "not_exists", "equals", "not_equals", "contains"]),
|
|
16059
16119
|
value: external_exports.string().max(2e3).default("")
|
|
16060
16120
|
}).strict(),
|
|
16061
16121
|
external_exports.object({
|
|
@@ -16118,6 +16178,7 @@ function __atcCheck(check, res) {
|
|
|
16118
16178
|
try { parsed = JSON.parse(res.body); } catch (e) { return false; }
|
|
16119
16179
|
var got = __atcJsonGet(parsed, check.path);
|
|
16120
16180
|
if (check.op === "exists") return got.found;
|
|
16181
|
+
if (check.op === "not_exists") return !got.found;
|
|
16121
16182
|
if (!got.found) return check.op === "not_equals";
|
|
16122
16183
|
var text = __atcAsText(got.value);
|
|
16123
16184
|
if (check.op === "equals") return text === check.value;
|
|
@@ -16307,18 +16368,18 @@ var init_definition = __esm({
|
|
|
16307
16368
|
thinkTimeMs: external_exports.number().min(0).default(0),
|
|
16308
16369
|
thinkJitterMs: external_exports.number().min(0).default(0),
|
|
16309
16370
|
gracefulStop: durationSchema.default("10s")
|
|
16310
|
-
}).strict().superRefine((
|
|
16311
|
-
if ((
|
|
16371
|
+
}).strict().superRefine((load3, ctx) => {
|
|
16372
|
+
if ((load3.executor === "ramping-vus" || load3.executor === "ramping-arrival-rate") && load3.stages.length === 0) {
|
|
16312
16373
|
ctx.addIssue({
|
|
16313
16374
|
code: external_exports.ZodIssueCode.custom,
|
|
16314
|
-
message: `${
|
|
16375
|
+
message: `${load3.executor} requires at least one stage`,
|
|
16315
16376
|
path: ["stages"]
|
|
16316
16377
|
});
|
|
16317
16378
|
}
|
|
16318
|
-
if (
|
|
16379
|
+
if (load3.executor === "constant-arrival-rate" && !load3.rate) {
|
|
16319
16380
|
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, message: "constant-arrival-rate needs a rate", path: ["rate"] });
|
|
16320
16381
|
}
|
|
16321
|
-
if (!isArrivalRate(
|
|
16382
|
+
if (!isArrivalRate(load3.executor) && load3.stages.some((s) => s.target > 500)) {
|
|
16322
16383
|
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, message: "A stage can use at most 500 virtual users", path: ["stages"] });
|
|
16323
16384
|
}
|
|
16324
16385
|
});
|
|
@@ -16341,9 +16402,11 @@ var init_definition = __esm({
|
|
|
16341
16402
|
maxRedirects: external_exports.number().int().min(0).max(20).default(5),
|
|
16342
16403
|
httpVersion: external_exports.enum(["1.1", "2"]).default("1.1"),
|
|
16343
16404
|
tlsSkipVerify: external_exports.boolean().default(false),
|
|
16344
|
-
connectionReuse: external_exports.boolean().default(true)
|
|
16405
|
+
connectionReuse: external_exports.boolean().default(true),
|
|
16406
|
+
/** Send cookies from the jar and keep the ones the API sets (like a browser). */
|
|
16407
|
+
cookies: external_exports.boolean().default(true)
|
|
16345
16408
|
}).strict();
|
|
16346
|
-
oauthGrants = ["client_credentials", "password", "authorization_code"];
|
|
16409
|
+
oauthGrants = ["client_credentials", "password", "authorization_code", "jwt_bearer", "saml_bearer"];
|
|
16347
16410
|
oauthConfigSchema = external_exports.object({
|
|
16348
16411
|
grant: external_exports.enum(oauthGrants),
|
|
16349
16412
|
tokenUrl: external_exports.string().url(),
|
|
@@ -16355,7 +16418,25 @@ var init_definition = __esm({
|
|
|
16355
16418
|
username: external_exports.string().default(""),
|
|
16356
16419
|
password: external_exports.string().default(""),
|
|
16357
16420
|
/** How the client id and secret go to the token endpoint. */
|
|
16358
|
-
|
|
16421
|
+
/** basic / body: the client secret; private_key_jwt: a JWT signed with the private key (client assertion). */
|
|
16422
|
+
clientAuth: external_exports.enum(["basic", "body", "private_key_jwt"]).default("basic"),
|
|
16423
|
+
// Signed assertions (JWT bearer grant, client assertion).
|
|
16424
|
+
privateKey: external_exports.string().default(""),
|
|
16425
|
+
assertionAlgorithm: external_exports.enum(["RS256", "RS384", "RS512", "PS256", "ES256", "ES384"]).default("RS256"),
|
|
16426
|
+
issuer: external_exports.string().default(""),
|
|
16427
|
+
subject: external_exports.string().default(""),
|
|
16428
|
+
audience: external_exports.string().default(""),
|
|
16429
|
+
kid: external_exports.string().default(""),
|
|
16430
|
+
/** The certificate (PEM) of the key: its thumbprint goes in the JWT header (x5t), as Azure AD wants. */
|
|
16431
|
+
certificate: external_exports.string().default(""),
|
|
16432
|
+
/** More claims for the JWT bearer assertion, as JSON. */
|
|
16433
|
+
extraClaims: external_exports.string().default(""),
|
|
16434
|
+
// SAML 2.0 bearer (SAP SuccessFactors).
|
|
16435
|
+
samlSource: external_exports.enum(["idp", "assertion"]).default("idp"),
|
|
16436
|
+
idpUrl: external_exports.string().default(""),
|
|
16437
|
+
userId: external_exports.string().default(""),
|
|
16438
|
+
companyId: external_exports.string().default(""),
|
|
16439
|
+
assertion: external_exports.string().default(""),
|
|
16359
16440
|
pkce: external_exports.boolean().default(true),
|
|
16360
16441
|
/** Extra token-request fields, e.g. audience or resource. */
|
|
16361
16442
|
extra: external_exports.array(keyValueSchema).max(20).default([])
|
|
@@ -16376,6 +16457,17 @@ var init_definition = __esm({
|
|
|
16376
16457
|
headerName: external_exports.string().min(1).default("Authorization"),
|
|
16377
16458
|
prefix: external_exports.string().default("Bearer")
|
|
16378
16459
|
}).strict(),
|
|
16460
|
+
// OAuth 1.0 (RFC 5849): every request signed with the consumer and token secrets (NetSuite TBA…).
|
|
16461
|
+
external_exports.object({
|
|
16462
|
+
mode: external_exports.literal("oauth1"),
|
|
16463
|
+
consumerKey: external_exports.string().min(1),
|
|
16464
|
+
consumerSecret: external_exports.string(),
|
|
16465
|
+
token: external_exports.string().default(""),
|
|
16466
|
+
tokenSecret: external_exports.string().default(""),
|
|
16467
|
+
signatureMethod: external_exports.enum(["HMAC-SHA256", "HMAC-SHA1", "PLAINTEXT"]).default("HMAC-SHA256"),
|
|
16468
|
+
realm: external_exports.string().default(""),
|
|
16469
|
+
addTo: external_exports.enum(["header", "query"]).default("header")
|
|
16470
|
+
}).strict(),
|
|
16379
16471
|
// AWS Signature Version 4: every request is signed (API Gateway, S3, Lambda function URLs…).
|
|
16380
16472
|
external_exports.object({
|
|
16381
16473
|
mode: external_exports.literal("aws_sigv4"),
|
|
@@ -24007,9 +24099,14 @@ function defaultRequestDraft() {
|
|
|
24007
24099
|
params: [],
|
|
24008
24100
|
headers: [],
|
|
24009
24101
|
bodyMode: "none",
|
|
24102
|
+
graphqlQuery: "",
|
|
24103
|
+
graphqlVariables: "",
|
|
24104
|
+
graphqlOperation: "",
|
|
24105
|
+
graphqlErrorsFail: true,
|
|
24010
24106
|
bodyText: "",
|
|
24011
24107
|
authMode: "inherit",
|
|
24012
24108
|
oauth: defaultOAuth(),
|
|
24109
|
+
oauth1: defaultOAuth1(),
|
|
24013
24110
|
aws: defaultAws(),
|
|
24014
24111
|
jwt: defaultJwt(),
|
|
24015
24112
|
domain: "",
|
|
@@ -24022,6 +24119,7 @@ function defaultRequestDraft() {
|
|
|
24022
24119
|
timeout: "60s",
|
|
24023
24120
|
maxRedirects: 5,
|
|
24024
24121
|
connectionReuse: true,
|
|
24122
|
+
useCookies: true,
|
|
24025
24123
|
tlsSkip: false,
|
|
24026
24124
|
checks: [],
|
|
24027
24125
|
before: null,
|
|
@@ -24064,8 +24162,8 @@ function requestFromUnknown(value) {
|
|
|
24064
24162
|
const base = defaultRequestDraft();
|
|
24065
24163
|
if (!value || typeof value !== "object") return base;
|
|
24066
24164
|
const item = value;
|
|
24067
|
-
const bodyMode = item.bodyMode === "json" || item.bodyMode === "text" ? item.bodyMode : "none";
|
|
24068
|
-
const authMode2 = item.authMode === "bearer" || item.authMode === "basic" || item.authMode === "api_key" || item.authMode === "inherit" || item.authMode === "oauth2" || item.authMode === "digest" || item.authMode === "ntlm" || item.authMode === "aws_sigv4" || item.authMode === "jwt" ? item.authMode : item.authMode === void 0 ? "inherit" : "none";
|
|
24165
|
+
const bodyMode = item.bodyMode === "json" || item.bodyMode === "text" || item.bodyMode === "graphql" ? item.bodyMode : "none";
|
|
24166
|
+
const authMode2 = item.authMode === "bearer" || item.authMode === "basic" || item.authMode === "api_key" || item.authMode === "inherit" || item.authMode === "oauth2" || item.authMode === "digest" || item.authMode === "ntlm" || item.authMode === "aws_sigv4" || item.authMode === "jwt" || item.authMode === "oauth1" ? item.authMode : item.authMode === void 0 ? "inherit" : "none";
|
|
24069
24167
|
return {
|
|
24070
24168
|
method: METHODS.includes(item.method) ? item.method : "GET",
|
|
24071
24169
|
url: str(item.url, ""),
|
|
@@ -24075,8 +24173,13 @@ function requestFromUnknown(value) {
|
|
|
24075
24173
|
headers: rows(item.headers),
|
|
24076
24174
|
bodyMode,
|
|
24077
24175
|
bodyText: str(item.bodyText, ""),
|
|
24176
|
+
graphqlQuery: str(item.graphqlQuery, ""),
|
|
24177
|
+
graphqlVariables: str(item.graphqlVariables, ""),
|
|
24178
|
+
graphqlOperation: str(item.graphqlOperation, ""),
|
|
24179
|
+
graphqlErrorsFail: item.graphqlErrorsFail !== false,
|
|
24078
24180
|
authMode: authMode2,
|
|
24079
24181
|
oauth: oauthFromUnknown(item.oauth),
|
|
24182
|
+
oauth1: oauth1FromUnknown(item.oauth1),
|
|
24080
24183
|
aws: awsFromUnknown(item.aws),
|
|
24081
24184
|
jwt: jwtFromUnknown(item.jwt),
|
|
24082
24185
|
domain: str(item.domain, ""),
|
|
@@ -24089,6 +24192,7 @@ function requestFromUnknown(value) {
|
|
|
24089
24192
|
timeout: str(item.timeout, base.timeout),
|
|
24090
24193
|
maxRedirects: num(item.maxRedirects, base.maxRedirects),
|
|
24091
24194
|
connectionReuse: item.connectionReuse !== false,
|
|
24195
|
+
useCookies: item.useCookies !== false,
|
|
24092
24196
|
tlsSkip: item.tlsSkip === true,
|
|
24093
24197
|
checks: checksFrom(item.checks),
|
|
24094
24198
|
before: beforeFrom(item.before),
|
|
@@ -24099,12 +24203,12 @@ function requestFromUnknown(value) {
|
|
|
24099
24203
|
})
|
|
24100
24204
|
};
|
|
24101
24205
|
}
|
|
24102
|
-
function applyPreset(
|
|
24103
|
-
if (preset === "custom") return { ...
|
|
24104
|
-
return { preset, mode: "users", rps:
|
|
24206
|
+
function applyPreset(load3, preset) {
|
|
24207
|
+
if (preset === "custom") return { ...load3, preset };
|
|
24208
|
+
return { preset, mode: "users", rps: load3.rps, maxVus: load3.maxVus, stages: null, ...LOAD_KNOB_PRESETS[preset] };
|
|
24105
24209
|
}
|
|
24106
|
-
function toLoadConfig(
|
|
24107
|
-
const { preset: _preset, ...knobs } =
|
|
24210
|
+
function toLoadConfig(load3) {
|
|
24211
|
+
const { preset: _preset, ...knobs } = load3;
|
|
24108
24212
|
return loadFromLabKnobs(knobs);
|
|
24109
24213
|
}
|
|
24110
24214
|
function withUrl(draft, value) {
|
|
@@ -24119,12 +24223,12 @@ function hasBody(method) {
|
|
|
24119
24223
|
function draftRuntimeNames(draft) {
|
|
24120
24224
|
return runtimeVariableNames({ setup: draft.before, data: draft.testData });
|
|
24121
24225
|
}
|
|
24122
|
-
function buildPayload(draft,
|
|
24226
|
+
function buildPayload(draft, load3, name, targets) {
|
|
24123
24227
|
const parts = requestParts(draft);
|
|
24124
24228
|
return {
|
|
24125
24229
|
name: name.slice(0, 120),
|
|
24126
24230
|
...parts,
|
|
24127
|
-
load:
|
|
24231
|
+
load: load3,
|
|
24128
24232
|
targets,
|
|
24129
24233
|
setup: void 0,
|
|
24130
24234
|
data: draft.testData && draft.testData.rows.length ? draft.testData : void 0,
|
|
@@ -24178,6 +24282,11 @@ function requestParts(draft) {
|
|
|
24178
24282
|
if (!a.accessKeyId.trim() || !a.secretAccessKey) throw new Error("AWS signing needs the access key ID and the secret access key");
|
|
24179
24283
|
if (!a.region.trim() || !a.service.trim()) throw new Error("AWS signing needs the region (like eu-west-1) and the service (like execute-api or s3)");
|
|
24180
24284
|
auth = { mode: "aws_sigv4", accessKeyId: a.accessKeyId.trim(), secretAccessKey: a.secretAccessKey, sessionToken: a.sessionToken.trim(), region: a.region.trim(), service: a.service.trim() };
|
|
24285
|
+
} else if (draft.authMode === "oauth1") {
|
|
24286
|
+
const o = draft.oauth1;
|
|
24287
|
+
if (!o.consumerKey.trim()) throw new Error("OAuth 1.0 needs the consumer key");
|
|
24288
|
+
if (o.token.trim() && !o.tokenSecret) throw new Error("OAuth 1.0: the token needs its token secret");
|
|
24289
|
+
auth = { mode: "oauth1", consumerKey: o.consumerKey.trim(), consumerSecret: o.consumerSecret, token: o.token.trim(), tokenSecret: o.tokenSecret, signatureMethod: o.signatureMethod, realm: o.realm.trim(), addTo: o.addTo };
|
|
24181
24290
|
} else if (draft.authMode === "jwt") {
|
|
24182
24291
|
const j = draft.jwt;
|
|
24183
24292
|
const hmac3 = j.algorithm.startsWith("HS");
|
|
@@ -24200,6 +24309,17 @@ function requestParts(draft) {
|
|
|
24200
24309
|
if (bare) throw new Error(`Put quotes around {{${bare}}} in the JSON body \u2014 values from test data and run-first steps are filled in as text`);
|
|
24201
24310
|
throw new Error(`Body is not valid JSON: ${err instanceof Error ? err.message : "parse error"}`);
|
|
24202
24311
|
}
|
|
24312
|
+
} else if (hasBody(draft.method) && draft.bodyMode === "graphql") {
|
|
24313
|
+
if (!draft.graphqlQuery.trim()) throw new Error("Write the GraphQL query");
|
|
24314
|
+
let variables = {};
|
|
24315
|
+
if (draft.graphqlVariables.trim()) {
|
|
24316
|
+
try {
|
|
24317
|
+
variables = JSON.parse(draft.graphqlVariables);
|
|
24318
|
+
} catch (err) {
|
|
24319
|
+
throw new Error(`GraphQL variables are not valid JSON: ${err instanceof Error ? err.message : "parse error"} \u2014 put quotes around "{{values}}" from test data`);
|
|
24320
|
+
}
|
|
24321
|
+
}
|
|
24322
|
+
body = { mode: "json", json: { query: draft.graphqlQuery, variables, ...draft.graphqlOperation.trim() ? { operationName: draft.graphqlOperation.trim() } : {} } };
|
|
24203
24323
|
} else if (hasBody(draft.method) && draft.bodyMode === "text") {
|
|
24204
24324
|
body = { mode: "text", text: draft.bodyText };
|
|
24205
24325
|
}
|
|
@@ -24211,7 +24331,11 @@ function requestParts(draft) {
|
|
|
24211
24331
|
headers: draft.headers.filter((h) => h.key.trim()),
|
|
24212
24332
|
body,
|
|
24213
24333
|
expectedStatus: [],
|
|
24214
|
-
|
|
24334
|
+
// GraphQL answers 200 even when the query failed; "errors" in the answer is the failure.
|
|
24335
|
+
checks: [
|
|
24336
|
+
...usableChecks(draft.checks),
|
|
24337
|
+
...hasBody(draft.method) && draft.bodyMode === "graphql" && draft.graphqlErrorsFail ? [{ type: "json", path: "errors", op: "not_exists", value: "" }] : []
|
|
24338
|
+
]
|
|
24215
24339
|
},
|
|
24216
24340
|
auth,
|
|
24217
24341
|
options: {
|
|
@@ -24219,7 +24343,8 @@ function requestParts(draft) {
|
|
|
24219
24343
|
maxRedirects: draft.maxRedirects,
|
|
24220
24344
|
httpVersion: "1.1",
|
|
24221
24345
|
tlsSkipVerify: draft.tlsSkip,
|
|
24222
|
-
connectionReuse: draft.connectionReuse
|
|
24346
|
+
connectionReuse: draft.connectionReuse,
|
|
24347
|
+
cookies: draft.useCookies
|
|
24223
24348
|
}
|
|
24224
24349
|
};
|
|
24225
24350
|
}
|
|
@@ -24250,22 +24375,22 @@ function durationError(value, allowZero = true) {
|
|
|
24250
24375
|
if (!allowZero && /^0+(\.0+)?(ms|s|m|h)$/.test(value.trim())) return "Must be longer than 0";
|
|
24251
24376
|
return void 0;
|
|
24252
24377
|
}
|
|
24253
|
-
function loadProblem(
|
|
24254
|
-
if (
|
|
24255
|
-
for (const [i, stage] of
|
|
24378
|
+
function loadProblem(load3) {
|
|
24379
|
+
if (load3.stages && load3.stages.length) {
|
|
24380
|
+
for (const [i, stage] of load3.stages.entries()) {
|
|
24256
24381
|
const bad = durationError(stage.duration, false);
|
|
24257
24382
|
if (bad) return `Stage ${i + 1}: ${bad}`;
|
|
24258
24383
|
if (!(stage.target >= 0)) return `Stage ${i + 1}: target must be 0 or more`;
|
|
24259
|
-
if (
|
|
24384
|
+
if (load3.mode !== "rate" && stage.target > 500) return `Stage ${i + 1}: at most 500 virtual users`;
|
|
24260
24385
|
}
|
|
24261
|
-
if (!
|
|
24262
|
-
return durationError(
|
|
24386
|
+
if (!load3.stages.some((s) => s.target > 0)) return "At least one stage needs a target above 0";
|
|
24387
|
+
return durationError(load3.gracefulStop) ?? null;
|
|
24263
24388
|
}
|
|
24264
|
-
if (
|
|
24265
|
-
if (!(
|
|
24266
|
-
if (!(
|
|
24267
|
-
} else if (!(
|
|
24268
|
-
return durationError(
|
|
24389
|
+
if (load3.mode === "rate") {
|
|
24390
|
+
if (!(load3.rps >= 1 && load3.rps <= 1e5)) return "Requests per second must be 1\u2013100,000";
|
|
24391
|
+
if (!(load3.maxVus >= 1 && load3.maxVus <= 2e3)) return "Max virtual users must be 1\u20132,000";
|
|
24392
|
+
} else if (!(load3.vus >= 1 && load3.vus <= 500)) return "Virtual users must be 1\u2013500";
|
|
24393
|
+
return durationError(load3.duration, false) ?? durationError(load3.rampUp) ?? durationError(load3.rampDown) ?? durationError(load3.gracefulStop) ?? null;
|
|
24269
24394
|
}
|
|
24270
24395
|
function applyFolders(draft, folders) {
|
|
24271
24396
|
let next = draft;
|
|
@@ -24275,7 +24400,7 @@ function applyFolders(draft, folders) {
|
|
|
24275
24400
|
const a = from?.settings.auth;
|
|
24276
24401
|
if (from && a && a.authMode !== "none") {
|
|
24277
24402
|
authFrom = from.path;
|
|
24278
|
-
next = { ...next, authMode: a.authMode, oauth: a.oauth, aws: a.aws, jwt: a.jwt, domain: a.domain, token: a.token, username: a.username, password: a.password, apiName: a.apiName, apiValue: a.apiValue, apiIn: a.apiIn };
|
|
24403
|
+
next = { ...next, authMode: a.authMode, oauth: a.oauth, oauth1: a.oauth1, aws: a.aws, jwt: a.jwt, domain: a.domain, token: a.token, username: a.username, password: a.password, apiName: a.apiName, apiValue: a.apiValue, apiIn: a.apiIn };
|
|
24279
24404
|
} else {
|
|
24280
24405
|
next = { ...next, authMode: "none" };
|
|
24281
24406
|
}
|
|
@@ -24298,6 +24423,20 @@ function oauthWire(o) {
|
|
|
24298
24423
|
throw new Error("The redirect URL must be on this computer, like http://127.0.0.1:53682/callback \u2014 add the same one in your OAuth app");
|
|
24299
24424
|
}
|
|
24300
24425
|
if (!o.clientId.trim()) throw new Error("OAuth 2.0 needs a client ID");
|
|
24426
|
+
const needsKey = o.grant === "jwt_bearer" || o.clientAuth === "private_key_jwt" || o.grant === "saml_bearer" && o.samlSource === "idp";
|
|
24427
|
+
if (needsKey && !o.privateKey.trim()) throw new Error("OAuth 2.0: this sign-in needs the private key (PEM)");
|
|
24428
|
+
if (o.grant === "jwt_bearer" && o.extraClaims.trim()) {
|
|
24429
|
+
try {
|
|
24430
|
+
const c = JSON.parse(o.extraClaims);
|
|
24431
|
+
if (!c || typeof c !== "object" || Array.isArray(c)) throw new Error("x");
|
|
24432
|
+
} catch {
|
|
24433
|
+
throw new Error("OAuth 2.0: the extra claims must be a JSON object");
|
|
24434
|
+
}
|
|
24435
|
+
}
|
|
24436
|
+
if (o.grant === "saml_bearer" && o.samlSource === "idp" && (!/^https?:\/\//i.test(o.idpUrl.trim()) || !o.userId.trim())) {
|
|
24437
|
+
throw new Error("SAML bearer needs the assertion URL (SuccessFactors: https://<api-server>/oauth/idp) and the user ID");
|
|
24438
|
+
}
|
|
24439
|
+
if (o.grant === "saml_bearer" && o.samlSource === "assertion" && !o.assertion.trim()) throw new Error("SAML bearer needs the assertion (base64)");
|
|
24301
24440
|
return {
|
|
24302
24441
|
grant: o.grant,
|
|
24303
24442
|
tokenUrl,
|
|
@@ -24310,7 +24449,20 @@ function oauthWire(o) {
|
|
|
24310
24449
|
password: o.password,
|
|
24311
24450
|
clientAuth: o.clientAuth,
|
|
24312
24451
|
pkce: o.pkce,
|
|
24313
|
-
extra: o.extra.filter((e) => e.key.trim())
|
|
24452
|
+
extra: o.extra.filter((e) => e.key.trim()),
|
|
24453
|
+
privateKey: o.privateKey.trim(),
|
|
24454
|
+
assertionAlgorithm: o.assertionAlgorithm,
|
|
24455
|
+
issuer: o.issuer.trim(),
|
|
24456
|
+
subject: o.subject.trim(),
|
|
24457
|
+
audience: o.audience.trim(),
|
|
24458
|
+
kid: o.kid.trim(),
|
|
24459
|
+
certificate: o.certificate.trim(),
|
|
24460
|
+
extraClaims: o.extraClaims.trim(),
|
|
24461
|
+
samlSource: o.samlSource,
|
|
24462
|
+
idpUrl: o.idpUrl.trim(),
|
|
24463
|
+
userId: o.userId.trim(),
|
|
24464
|
+
companyId: o.companyId.trim(),
|
|
24465
|
+
assertion: o.assertion.trim()
|
|
24314
24466
|
};
|
|
24315
24467
|
}
|
|
24316
24468
|
var METHODS, DEFAULT_LOAD, str, num, TEMPLATED, DURATION;
|
|
@@ -24334,12 +24486,15 @@ var init_draft = __esm({
|
|
|
24334
24486
|
"params",
|
|
24335
24487
|
"headers",
|
|
24336
24488
|
"bodyText",
|
|
24489
|
+
"graphqlQuery",
|
|
24490
|
+
"graphqlVariables",
|
|
24337
24491
|
"token",
|
|
24338
24492
|
"username",
|
|
24339
24493
|
"password",
|
|
24340
24494
|
"apiName",
|
|
24341
24495
|
"apiValue",
|
|
24342
24496
|
"oauth",
|
|
24497
|
+
"oauth1",
|
|
24343
24498
|
"aws",
|
|
24344
24499
|
"jwt",
|
|
24345
24500
|
"domain"
|
|
@@ -25189,10 +25344,10 @@ function checksRate(r) {
|
|
|
25189
25344
|
return passes + fails ? passes / (passes + fails) : null;
|
|
25190
25345
|
}
|
|
25191
25346
|
function plannedLoad(r) {
|
|
25192
|
-
const
|
|
25193
|
-
const rate =
|
|
25194
|
-
const peak = rate ?
|
|
25195
|
-
const ms3 =
|
|
25347
|
+
const load3 = r.definition.load;
|
|
25348
|
+
const rate = load3.executor.endsWith("arrival-rate");
|
|
25349
|
+
const peak = rate ? load3.rate ?? Math.max(0, ...load3.stages.map((s) => s.target)) : Math.max(0, ...load3.stages.map((s) => s.target));
|
|
25350
|
+
const ms3 = load3.stages.reduce((n, s) => n + durationToMs(s.duration), 0);
|
|
25196
25351
|
return { rate, peak, ms: ms3 };
|
|
25197
25352
|
}
|
|
25198
25353
|
function fairness(before, after) {
|
|
@@ -25473,6 +25628,90 @@ function __atcSign(method, url, headers, body) {
|
|
|
25473
25628
|
}
|
|
25474
25629
|
});
|
|
25475
25630
|
|
|
25631
|
+
// ../shared/src/oauth1.ts
|
|
25632
|
+
function pctEncode(s) {
|
|
25633
|
+
return encodeURIComponent(s).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
25634
|
+
}
|
|
25635
|
+
function decodeForm(s) {
|
|
25636
|
+
try {
|
|
25637
|
+
return decodeURIComponent(s.replace(/\+/g, " "));
|
|
25638
|
+
} catch {
|
|
25639
|
+
return s;
|
|
25640
|
+
}
|
|
25641
|
+
}
|
|
25642
|
+
function formPairs(text) {
|
|
25643
|
+
return text.replace(/^\?/, "").split("&").filter(Boolean).map((p) => {
|
|
25644
|
+
const i = p.indexOf("=");
|
|
25645
|
+
return [decodeForm(i < 0 ? p : p.slice(0, i)), decodeForm(i < 0 ? "" : p.slice(i + 1))];
|
|
25646
|
+
});
|
|
25647
|
+
}
|
|
25648
|
+
function oauth1BaseString(method, url2, params) {
|
|
25649
|
+
const m = /^(https?):\/\/([^/?#]+)([^?#]*)/i.exec(url2);
|
|
25650
|
+
if (!m) throw new Error("OAuth 1.0 needs a full http(s) address");
|
|
25651
|
+
const scheme = m[1].toLowerCase();
|
|
25652
|
+
let host2 = m[2].toLowerCase();
|
|
25653
|
+
if (scheme === "https" && host2.endsWith(":443") || scheme === "http" && host2.endsWith(":80")) host2 = host2.replace(/:\d+$/, "");
|
|
25654
|
+
const base = `${scheme}://${host2}${m[3] || "/"}`;
|
|
25655
|
+
const normalized = params.map(([k, v]) => [pctEncode(k), pctEncode(v)]).sort(([a, av], [b, bv]) => a < b ? -1 : a > b ? 1 : av < bv ? -1 : av > bv ? 1 : 0).map(([k, v]) => `${k}=${v}`).join("&");
|
|
25656
|
+
return [method.toUpperCase(), pctEncode(base), pctEncode(normalized)].join("&");
|
|
25657
|
+
}
|
|
25658
|
+
function oauth1Params(c, nonce, timestamp) {
|
|
25659
|
+
const p = [
|
|
25660
|
+
["oauth_consumer_key", c.consumerKey],
|
|
25661
|
+
["oauth_nonce", nonce],
|
|
25662
|
+
["oauth_signature_method", c.signatureMethod],
|
|
25663
|
+
["oauth_timestamp", String(timestamp)],
|
|
25664
|
+
["oauth_version", "1.0"]
|
|
25665
|
+
];
|
|
25666
|
+
if (c.token) p.push(["oauth_token", c.token]);
|
|
25667
|
+
return p;
|
|
25668
|
+
}
|
|
25669
|
+
function oauth1SigningKey(c) {
|
|
25670
|
+
return `${pctEncode(c.consumerSecret)}&${pctEncode(c.tokenSecret)}`;
|
|
25671
|
+
}
|
|
25672
|
+
function oauth1Header(c, oauth) {
|
|
25673
|
+
const parts = oauth.map(([k, v]) => `${pctEncode(k)}="${pctEncode(v)}"`);
|
|
25674
|
+
return `OAuth ${c.realm ? `realm="${c.realm.replace(/"/g, "")}", ` : ""}${parts.join(", ")}`;
|
|
25675
|
+
}
|
|
25676
|
+
var OAUTH1_JS;
|
|
25677
|
+
var init_oauth1 = __esm({
|
|
25678
|
+
"../shared/src/oauth1.ts"() {
|
|
25679
|
+
"use strict";
|
|
25680
|
+
OAUTH1_JS = `
|
|
25681
|
+
function __atcPct(s) { return encodeURIComponent(s).replace(/[!'()*]/g, function (c) { return "%" + c.charCodeAt(0).toString(16).toUpperCase(); }); }
|
|
25682
|
+
function __atcForm(t) { return t.replace(/^\\?/, "").split("&").filter(Boolean).map(function (p) { var i = p.indexOf("="); var d = function (x) { try { return decodeURIComponent(x.replace(/\\+/g, " ")); } catch (e) { return x; } }; return [d(i < 0 ? p : p.slice(0, i)), d(i < 0 ? "" : p.slice(i + 1))]; }); }
|
|
25683
|
+
function __atcOAuth1(method, url, headers, body) {
|
|
25684
|
+
var c = OAUTH1;
|
|
25685
|
+
var m = /^(https?):\\/\\/([^/?#]+)([^?#]*)(\\?[^#]*)?/i.exec(url);
|
|
25686
|
+
var scheme = m[1].toLowerCase(), host = m[2].toLowerCase();
|
|
25687
|
+
if ((scheme === "https" && /:443$/.test(host)) || (scheme === "http" && /:80$/.test(host))) host = host.replace(/:\\d+$/, "");
|
|
25688
|
+
var nonce = ""; var abc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
25689
|
+
for (var i = 0; i < 32; i++) nonce += abc.charAt(Math.floor(Math.random() * abc.length));
|
|
25690
|
+
var oauth = [["oauth_consumer_key", c.consumerKey], ["oauth_nonce", nonce], ["oauth_signature_method", c.signatureMethod], ["oauth_timestamp", String(Math.floor(Date.now() / 1000))], ["oauth_version", "1.0"]];
|
|
25691
|
+
if (c.token) oauth.push(["oauth_token", c.token]);
|
|
25692
|
+
var params = oauth.concat(__atcForm(m[4] || ""));
|
|
25693
|
+
var ct = ""; for (var k in headers) if (k.toLowerCase() === "content-type") ct = String(headers[k]);
|
|
25694
|
+
if (body && ct.indexOf("application/x-www-form-urlencoded") === 0) params = params.concat(__atcForm(body));
|
|
25695
|
+
var norm = params.map(function (p) { return [__atcPct(p[0]), __atcPct(p[1])]; }).sort(function (a, b) { return a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0; }).map(function (p) { return p[0] + "=" + p[1]; }).join("&");
|
|
25696
|
+
var baseString = [method.toUpperCase(), __atcPct(scheme + "://" + host + (m[3] || "/")), __atcPct(norm)].join("&");
|
|
25697
|
+
var key = __atcPct(c.consumerSecret) + "&" + __atcPct(c.tokenSecret);
|
|
25698
|
+
var sig = c.signatureMethod === "PLAINTEXT" ? key : crypto.hmac(c.signatureMethod === "HMAC-SHA1" ? "sha1" : "sha256", key, baseString, "base64");
|
|
25699
|
+
oauth.push(["oauth_signature", sig]);
|
|
25700
|
+
if (c.addTo === "query") return url + (m[4] ? "&" : "?") + oauth.map(function (p) { return __atcPct(p[0]) + "=" + __atcPct(p[1]); }).join("&");
|
|
25701
|
+
headers.Authorization = "OAuth " + (c.realm ? 'realm="' + c.realm.replace(/"/g, "") + '", ' : "") + oauth.map(function (p) { return __atcPct(p[0]) + '="' + __atcPct(p[1]) + '"'; }).join(", ");
|
|
25702
|
+
return url;
|
|
25703
|
+
}
|
|
25704
|
+
`;
|
|
25705
|
+
}
|
|
25706
|
+
});
|
|
25707
|
+
|
|
25708
|
+
// ../shared/src/graphql.ts
|
|
25709
|
+
var init_graphql = __esm({
|
|
25710
|
+
"../shared/src/graphql.ts"() {
|
|
25711
|
+
"use strict";
|
|
25712
|
+
}
|
|
25713
|
+
});
|
|
25714
|
+
|
|
25476
25715
|
// ../shared/src/export-fields.ts
|
|
25477
25716
|
function roundN(n, digits = 2) {
|
|
25478
25717
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -26429,6 +26668,8 @@ var init_src = __esm({
|
|
|
26429
26668
|
init_before_after();
|
|
26430
26669
|
init_ci_templates();
|
|
26431
26670
|
init_sigv4();
|
|
26671
|
+
init_oauth1();
|
|
26672
|
+
init_graphql();
|
|
26432
26673
|
init_latency_bins();
|
|
26433
26674
|
init_checks3();
|
|
26434
26675
|
init_definition();
|
|
@@ -26878,21 +27119,21 @@ function k6Thresholds(targets, hasChecks) {
|
|
|
26878
27119
|
if (hasChecks && targets.checksRate != null) out.checks = [wrap(`rate>=${targets.checksRate}`)];
|
|
26879
27120
|
return out;
|
|
26880
27121
|
}
|
|
26881
|
-
function scenarioBlock(
|
|
26882
|
-
const common = { gracefulStop:
|
|
26883
|
-
switch (
|
|
27122
|
+
function scenarioBlock(load3) {
|
|
27123
|
+
const common = { gracefulStop: load3.gracefulStop };
|
|
27124
|
+
switch (load3.executor) {
|
|
26884
27125
|
case "constant-vus":
|
|
26885
|
-
return { executor: "constant-vus", vus:
|
|
27126
|
+
return { executor: "constant-vus", vus: load3.vus, duration: load3.duration, ...common };
|
|
26886
27127
|
case "ramping-vus":
|
|
26887
|
-
return { executor: "ramping-vus", startVUs:
|
|
27128
|
+
return { executor: "ramping-vus", startVUs: load3.startVUs ?? 0, stages: load3.stages, ...common };
|
|
26888
27129
|
case "constant-arrival-rate":
|
|
26889
27130
|
return {
|
|
26890
27131
|
executor: "constant-arrival-rate",
|
|
26891
|
-
rate:
|
|
27132
|
+
rate: load3.rate,
|
|
26892
27133
|
timeUnit: "1s",
|
|
26893
|
-
duration:
|
|
26894
|
-
preAllocatedVUs:
|
|
26895
|
-
maxVUs: Math.max(
|
|
27134
|
+
duration: load3.duration,
|
|
27135
|
+
preAllocatedVUs: load3.vus,
|
|
27136
|
+
maxVUs: Math.max(load3.vus, load3.maxVUs ?? load3.vus),
|
|
26896
27137
|
...common
|
|
26897
27138
|
};
|
|
26898
27139
|
case "ramping-arrival-rate":
|
|
@@ -26900,9 +27141,9 @@ function scenarioBlock(load2) {
|
|
|
26900
27141
|
executor: "ramping-arrival-rate",
|
|
26901
27142
|
startRate: 0,
|
|
26902
27143
|
timeUnit: "1s",
|
|
26903
|
-
preAllocatedVUs:
|
|
26904
|
-
maxVUs: Math.max(
|
|
26905
|
-
stages:
|
|
27144
|
+
preAllocatedVUs: load3.vus,
|
|
27145
|
+
maxVUs: Math.max(load3.vus, load3.maxVUs ?? load3.vus),
|
|
27146
|
+
stages: load3.stages,
|
|
26906
27147
|
...common
|
|
26907
27148
|
};
|
|
26908
27149
|
}
|
|
@@ -26950,13 +27191,20 @@ function generateK6Script(run2, extra2 = {}) {
|
|
|
26950
27191
|
}
|
|
26951
27192
|
if (run2.auth.mode === "bearer") {
|
|
26952
27193
|
env.ATC_TOKEN = normalizeBearerToken(run2.auth.token);
|
|
26953
|
-
if (extra2.oauthRefresh)
|
|
27194
|
+
if (extra2.oauthRefresh) {
|
|
27195
|
+
const { bodies, ...plan } = extra2.oauthRefresh;
|
|
27196
|
+
env.ATC_OAUTH = JSON.stringify(bodies ? { ...plan, start: bodies.start, stepSec: bodies.stepSec } : plan);
|
|
27197
|
+
if (bodies) files["oauth.json"] = JSON.stringify(bodies.list);
|
|
27198
|
+
}
|
|
26954
27199
|
} else if (run2.auth.mode === "basic") {
|
|
26955
27200
|
env.ATC_BASIC_USER = run2.auth.username;
|
|
26956
27201
|
env.ATC_BASIC_PASS = run2.auth.password;
|
|
26957
27202
|
} else if (run2.auth.mode === "api_key" && run2.auth.in === "header") {
|
|
26958
27203
|
env.ATC_API_NAME = run2.auth.name;
|
|
26959
27204
|
env.ATC_API_KEY = run2.auth.value;
|
|
27205
|
+
} else if (run2.auth.mode === "oauth1") {
|
|
27206
|
+
const { mode: _mode, ...creds } = run2.auth;
|
|
27207
|
+
env.ATC_OAUTH1 = JSON.stringify(creds);
|
|
26960
27208
|
} else if (run2.auth.mode === "aws_sigv4") {
|
|
26961
27209
|
const { mode: _mode, ...creds } = run2.auth;
|
|
26962
27210
|
env.ATC_AWS = JSON.stringify(creds);
|
|
@@ -26990,7 +27238,7 @@ function __atcRow() {
|
|
|
26990
27238
|
const script = `import http from "k6/http";
|
|
26991
27239
|
import { check, sleep } from "k6";
|
|
26992
27240
|
import encoding from "k6/encoding";
|
|
26993
|
-
${run2.auth.mode === "aws_sigv4" ? `import crypto from "k6/crypto";
|
|
27241
|
+
${run2.auth.mode === "aws_sigv4" || run2.auth.mode === "oauth1" ? `import crypto from "k6/crypto";
|
|
26994
27242
|
` : ""}${data ? `import exec from "k6/execution";
|
|
26995
27243
|
import { SharedArray } from "k6/data";
|
|
26996
27244
|
` : ""}
|
|
@@ -26999,18 +27247,23 @@ export const options = Object.assign(${optionsBlock(run2)}, __ENV.ATC_TLS_AUTH ?
|
|
|
26999
27247
|
${CHECK_EVALUATOR_JS}
|
|
27000
27248
|
${dataBlock}${run2.auth.mode === "aws_sigv4" ? `
|
|
27001
27249
|
const AWS = JSON.parse(__ENV.ATC_AWS);
|
|
27002
|
-
${SIGV4_JS}` : ""}
|
|
27250
|
+
${SIGV4_JS}` : ""}${run2.auth.mode === "oauth1" ? `
|
|
27251
|
+
const OAUTH1 = JSON.parse(__ENV.ATC_OAUTH1);
|
|
27252
|
+
${OAUTH1_JS}` : ""}
|
|
27003
27253
|
const CHECKS = JSON.parse(__ENV.ATC_CHECKS || "[]");
|
|
27004
27254
|
// JWT: tokens signed by the app for the whole run; each is used for its stretch of time.
|
|
27005
27255
|
const JWT = __ENV.ATC_JWT ? JSON.parse(__ENV.ATC_JWT) : null;
|
|
27006
27256
|
const JWTS = JWT ? JSON.parse(open("./jwt.json")) : [];
|
|
27007
27257
|
// OAuth 2.0: each simulated user gets a new token shortly before the current one runs out.
|
|
27008
27258
|
const OAUTH = __ENV.ATC_OAUTH ? JSON.parse(__ENV.ATC_OAUTH) : null;
|
|
27259
|
+
const OAUTH_BODIES = OAUTH && OAUTH.stepSec ? JSON.parse(open("./oauth.json")) : null;
|
|
27009
27260
|
let __atcToken = __ENV.ATC_TOKEN || "";
|
|
27010
27261
|
let __atcExpires = OAUTH ? OAUTH.expiresAt : 0;
|
|
27011
27262
|
function __atcFreshToken() {
|
|
27012
27263
|
if (OAUTH && Date.now() > __atcExpires - 30000) {
|
|
27013
|
-
const
|
|
27264
|
+
const body = OAUTH_BODIES ? OAUTH_BODIES[Math.max(0, Math.min(OAUTH_BODIES.length - 1, Math.floor((Date.now() / 1000 - OAUTH.start) / OAUTH.stepSec)))] : OAUTH.body;
|
|
27265
|
+
// responseType: the run may discard bodies to save memory, but the token is in this one.
|
|
27266
|
+
const r = http.post(OAUTH.tokenUrl, body, { headers: OAUTH.headers, responseType: "text", tags: { name: "OAuth token" } });
|
|
27014
27267
|
let json = null;
|
|
27015
27268
|
try { json = r.json(); } catch (e) { json = null; }
|
|
27016
27269
|
if (r.status >= 200 && r.status < 300 && json && json.access_token) {
|
|
@@ -27028,12 +27281,14 @@ export default function () {
|
|
|
27028
27281
|
${data ? 'const vars = __atcRow();\n const f = function (text, mode) { return __atcFill(text, vars, mode || "raw"); };' : "const f = function (text) { return text; };"}
|
|
27029
27282
|
const headers = JSON.parse(__ENV.ATC_HEADERS || "{}");
|
|
27030
27283
|
for (const key in headers) headers[key] = f(headers[key]);
|
|
27284
|
+
if (__ENV.ATC_COOKIE) headers.Cookie = __ENV.ATC_COOKIE;
|
|
27031
27285
|
if (__ENV.ATC_TOKEN) headers.Authorization = "Bearer " + f(__atcFreshToken());
|
|
27032
27286
|
if (__ENV.ATC_BASIC_USER !== undefined) headers.Authorization = "Basic " + encoding.b64encode(f(__ENV.ATC_BASIC_USER) + ":" + f(__ENV.ATC_BASIC_PASS || ""));
|
|
27033
27287
|
if (__ENV.ATC_API_NAME && __ENV.ATC_API_KEY) headers[f(__ENV.ATC_API_NAME)] = f(__ENV.ATC_API_KEY);
|
|
27034
27288
|
if (JWT) headers[JWT.headerName] = (JWT.prefix ? JWT.prefix + " " : "") + JWTS[Math.max(0, Math.min(JWTS.length - 1, Math.floor((Date.now() / 1000 - JWT.start) / JWT.stepSec)))];
|
|
27035
27289
|
const body = __ENV.ATC_BODY ? f(__ENV.ATC_BODY, __ENV.ATC_BODY_FILL) : null;
|
|
27036
27290
|
let target = f(__ENV.ATC_URL, "url");
|
|
27291
|
+
if (__ENV.ATC_OAUTH1) target = __atcOAuth1(__ENV.ATC_METHOD, target, headers, body);
|
|
27037
27292
|
if (__ENV.ATC_AWS) __atcSign(__ENV.ATC_METHOD, target, headers, body);
|
|
27038
27293
|
if (__ENV.ATC_AUTH_KIND) target = target.replace("://", "://" + encodeURIComponent(f(__ENV.ATC_AUTH_USER)) + ":" + encodeURIComponent(f(__ENV.ATC_AUTH_PASS || "")) + "@");
|
|
27039
27294
|
const res = http.request(__ENV.ATC_METHOD, target, body, {
|
|
@@ -27058,17 +27313,18 @@ export default function () {
|
|
|
27058
27313
|
`;
|
|
27059
27314
|
return { script, env, files };
|
|
27060
27315
|
}
|
|
27061
|
-
function loadDurationHintMs(
|
|
27062
|
-
if (
|
|
27063
|
-
return
|
|
27316
|
+
function loadDurationHintMs(load3) {
|
|
27317
|
+
if (load3.stages.length > 0) {
|
|
27318
|
+
return load3.stages.reduce((sum, stage) => sum + durationToMs(stage.duration), 0);
|
|
27064
27319
|
}
|
|
27065
|
-
return durationToMs(
|
|
27320
|
+
return durationToMs(load3.duration);
|
|
27066
27321
|
}
|
|
27067
27322
|
var init_k6_script = __esm({
|
|
27068
27323
|
"../shared/src/k6-script.ts"() {
|
|
27069
27324
|
"use strict";
|
|
27070
27325
|
init_chain();
|
|
27071
27326
|
init_checks3();
|
|
27327
|
+
init_oauth1();
|
|
27072
27328
|
init_sigv4();
|
|
27073
27329
|
init_definition();
|
|
27074
27330
|
}
|
|
@@ -44903,10 +45159,12 @@ var require_lib = __commonJS({
|
|
|
44903
45159
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
44904
45160
|
import { mkdir as mkdir2, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "node:fs/promises";
|
|
44905
45161
|
import { join as join4 } from "node:path";
|
|
44906
|
-
import {
|
|
44907
|
-
|
|
45162
|
+
import { X509Certificate } from "node:crypto";
|
|
45163
|
+
import * as tlsModule from "node:tls";
|
|
45164
|
+
import { connect as tlsSocket, createSecureContext, rootCertificates } from "node:tls";
|
|
45165
|
+
function useCertificateStore(dataDir, store3) {
|
|
44908
45166
|
dir = dataDir;
|
|
44909
|
-
secrets =
|
|
45167
|
+
secrets = store3;
|
|
44910
45168
|
}
|
|
44911
45169
|
function addTemporaryCertificate(host2, material) {
|
|
44912
45170
|
checkMaterial(material);
|
|
@@ -44928,23 +45186,104 @@ function hostMatches(pattern, url2) {
|
|
|
44928
45186
|
}
|
|
44929
45187
|
return name === host2;
|
|
44930
45188
|
}
|
|
44931
|
-
async function
|
|
44932
|
-
if (!dir) return [];
|
|
45189
|
+
async function readStore() {
|
|
45190
|
+
if (!dir) return { certificates: [], cas: [] };
|
|
44933
45191
|
try {
|
|
44934
45192
|
const raw = JSON.parse(await readFile2(join4(dir, "certificates.json"), "utf8"));
|
|
44935
|
-
return
|
|
45193
|
+
return {
|
|
45194
|
+
certificates: Array.isArray(raw.certificates) ? raw.certificates.filter((c) => c && typeof c.id === "string" && typeof c.host === "string") : [],
|
|
45195
|
+
cas: Array.isArray(raw.cas) ? raw.cas.filter((c) => c && typeof c.id === "string" && typeof c.pem === "string") : []
|
|
45196
|
+
};
|
|
44936
45197
|
} catch {
|
|
44937
|
-
return [];
|
|
45198
|
+
return { certificates: [], cas: [] };
|
|
44938
45199
|
}
|
|
44939
45200
|
}
|
|
44940
|
-
async function
|
|
45201
|
+
async function readAll() {
|
|
45202
|
+
return (await readStore()).certificates;
|
|
45203
|
+
}
|
|
45204
|
+
async function writeStore(next) {
|
|
44941
45205
|
await mkdir2(dir, { recursive: true });
|
|
44942
45206
|
const file2 = join4(dir, "certificates.json");
|
|
44943
45207
|
const tmp = `${file2}.${process.pid}.tmp`;
|
|
44944
|
-
await writeFile2(tmp, `${JSON.stringify({ version: 1,
|
|
45208
|
+
await writeFile2(tmp, `${JSON.stringify({ version: 1, ...next }, null, 2)}
|
|
44945
45209
|
`, "utf8");
|
|
44946
45210
|
await rename2(tmp, file2);
|
|
44947
45211
|
}
|
|
45212
|
+
async function writeAll(list) {
|
|
45213
|
+
await writeStore({ ...await readStore(), certificates: list });
|
|
45214
|
+
}
|
|
45215
|
+
async function listCas() {
|
|
45216
|
+
return (await readStore()).cas.map(({ pem: _pem, ...rest }) => rest);
|
|
45217
|
+
}
|
|
45218
|
+
async function addCas(pemText) {
|
|
45219
|
+
const blocks = pemText.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g) ?? [];
|
|
45220
|
+
if (!blocks.length) throw new Error("No certificate in that file \u2014 it should contain -----BEGIN CERTIFICATE-----");
|
|
45221
|
+
const store3 = await readStore();
|
|
45222
|
+
for (const pem of blocks) {
|
|
45223
|
+
let x;
|
|
45224
|
+
try {
|
|
45225
|
+
x = new X509Certificate(pem);
|
|
45226
|
+
} catch {
|
|
45227
|
+
throw new Error("A certificate in that file can't be read");
|
|
45228
|
+
}
|
|
45229
|
+
if (!x.ca) throw new Error(`${x.subject.split("\n").find((l) => l.startsWith("CN="))?.slice(3) ?? "That certificate"} is not a certificate authority \u2014 add the CA that issued the server's certificate`);
|
|
45230
|
+
if (store3.cas.some((c) => c.pem.replace(/\s/g, "") === pem.replace(/\s/g, ""))) continue;
|
|
45231
|
+
const cn = x.subject.split("\n").find((l) => l.startsWith("CN="))?.slice(3) ?? x.subject.replace(/\n/g, ", ");
|
|
45232
|
+
store3.cas.push({ id: randomUUID2(), subject: cn, expires: new Date(x.validTo).toISOString(), pem });
|
|
45233
|
+
}
|
|
45234
|
+
await writeStore(store3);
|
|
45235
|
+
casCache = null;
|
|
45236
|
+
return listCas();
|
|
45237
|
+
}
|
|
45238
|
+
async function removeCa(id) {
|
|
45239
|
+
const store3 = await readStore();
|
|
45240
|
+
if (!store3.cas.some((c) => c.id === id)) return false;
|
|
45241
|
+
await writeStore({ ...store3, cas: store3.cas.filter((c) => c.id !== id) });
|
|
45242
|
+
casCache = null;
|
|
45243
|
+
return true;
|
|
45244
|
+
}
|
|
45245
|
+
async function trustedCas() {
|
|
45246
|
+
casCache ??= (await readStore()).cas;
|
|
45247
|
+
return casCache.map((c) => c.pem);
|
|
45248
|
+
}
|
|
45249
|
+
function systemCas() {
|
|
45250
|
+
if (systemCache) return systemCache;
|
|
45251
|
+
const get = tlsModule.getCACertificates;
|
|
45252
|
+
try {
|
|
45253
|
+
systemCache = get ? get("system") : [];
|
|
45254
|
+
} catch {
|
|
45255
|
+
systemCache = [];
|
|
45256
|
+
}
|
|
45257
|
+
return systemCache;
|
|
45258
|
+
}
|
|
45259
|
+
async function trustPool() {
|
|
45260
|
+
const added = await trustedCas();
|
|
45261
|
+
const system = systemCas();
|
|
45262
|
+
return added.length || system.length ? [...rootCertificates, ...system, ...added] : null;
|
|
45263
|
+
}
|
|
45264
|
+
async function needsTrustedCa(target) {
|
|
45265
|
+
const extra2 = await trustedCas();
|
|
45266
|
+
let url2;
|
|
45267
|
+
try {
|
|
45268
|
+
url2 = new URL(target);
|
|
45269
|
+
} catch {
|
|
45270
|
+
return false;
|
|
45271
|
+
}
|
|
45272
|
+
if (!extra2.length || url2.protocol !== "https:") return false;
|
|
45273
|
+
const ok = (ca) => new Promise((resolve5) => {
|
|
45274
|
+
const sock = tlsSocket({ host: url2.hostname, port: Number(url2.port || 443), servername: url2.hostname, ca, timeout: 8e3, rejectUnauthorized: false }, () => {
|
|
45275
|
+
resolve5(sock.authorized);
|
|
45276
|
+
sock.end();
|
|
45277
|
+
});
|
|
45278
|
+
sock.on("error", () => resolve5(false));
|
|
45279
|
+
sock.on("timeout", () => {
|
|
45280
|
+
sock.destroy();
|
|
45281
|
+
resolve5(false);
|
|
45282
|
+
});
|
|
45283
|
+
});
|
|
45284
|
+
const system = systemCas();
|
|
45285
|
+
return !await ok(system.length ? [...rootCertificates, ...system] : void 0) && await ok([...rootCertificates, ...system, ...extra2]);
|
|
45286
|
+
}
|
|
44948
45287
|
async function listCertificates() {
|
|
44949
45288
|
return (await readAll()).map(({ cert: _cert, ...summary }) => summary);
|
|
44950
45289
|
}
|
|
@@ -45049,8 +45388,18 @@ async function k6TlsAuth(urls) {
|
|
|
45049
45388
|
}
|
|
45050
45389
|
async function tlsConnect(target, skipVerify = false) {
|
|
45051
45390
|
const m = await certificateFor(target);
|
|
45052
|
-
|
|
45053
|
-
|
|
45391
|
+
const pool = await trustPool();
|
|
45392
|
+
if (!m && !skipVerify && !pool) return void 0;
|
|
45393
|
+
return {
|
|
45394
|
+
...skipVerify ? { rejectUnauthorized: false } : {},
|
|
45395
|
+
// Added to the usual roots, not instead of them.
|
|
45396
|
+
...pool ? { ca: pool } : {},
|
|
45397
|
+
...m ? { cert: m.cert, key: m.key, pfx: m.pfx, passphrase: m.passphrase } : {}
|
|
45398
|
+
};
|
|
45399
|
+
}
|
|
45400
|
+
async function caBundle() {
|
|
45401
|
+
const cas = await trustedCas();
|
|
45402
|
+
return cas.length ? [...rootCertificates, ...systemCas(), ...cas].join("\n") : null;
|
|
45054
45403
|
}
|
|
45055
45404
|
function registerCertificateRoutes(app2) {
|
|
45056
45405
|
app2.get("/certificates", async () => listCertificates());
|
|
@@ -45067,12 +45416,27 @@ function registerCertificateRoutes(app2) {
|
|
|
45067
45416
|
"/certificates/:id",
|
|
45068
45417
|
async (request3, reply) => await deleteCertificate(request3.params.id) ? { removed: 1 } : reply.code(404).send({ error: "certificate not found" })
|
|
45069
45418
|
);
|
|
45419
|
+
app2.get("/certificates/cas", async () => listCas());
|
|
45420
|
+
app2.get("/certificates/system", async () => ({ count: systemCas().length }));
|
|
45421
|
+
app2.post("/certificates/cas", async (request3, reply) => {
|
|
45422
|
+
const pem = request3.body?.pem;
|
|
45423
|
+
if (typeof pem !== "string" || pem.length > 2e6) return reply.code(400).send({ error: "Send the CA file's text as pem" });
|
|
45424
|
+
try {
|
|
45425
|
+
return await addCas(pem);
|
|
45426
|
+
} catch (err) {
|
|
45427
|
+
return reply.code(422).send({ error: err instanceof Error ? err.message : String(err) });
|
|
45428
|
+
}
|
|
45429
|
+
});
|
|
45430
|
+
app2.delete(
|
|
45431
|
+
"/certificates/cas/:id",
|
|
45432
|
+
async (request3, reply) => await removeCa(request3.params.id) ? { removed: 1 } : reply.code(404).send({ error: "not found" })
|
|
45433
|
+
);
|
|
45070
45434
|
app2.get("/certificates/match", async (request3) => {
|
|
45071
45435
|
const m = await certificateFor(request3.query.url ?? "").catch(() => null);
|
|
45072
45436
|
return { host: m?.host ?? null };
|
|
45073
45437
|
});
|
|
45074
45438
|
}
|
|
45075
|
-
var import_node_forge, dir, secrets, extra, saveBody;
|
|
45439
|
+
var import_node_forge, dir, secrets, extra, casCache, systemCache, saveBody;
|
|
45076
45440
|
var init_certificates = __esm({
|
|
45077
45441
|
"../../apps/engine/src/certificates.ts"() {
|
|
45078
45442
|
"use strict";
|
|
@@ -45081,6 +45445,8 @@ var init_certificates = __esm({
|
|
|
45081
45445
|
dir = "";
|
|
45082
45446
|
secrets = null;
|
|
45083
45447
|
extra = [];
|
|
45448
|
+
casCache = null;
|
|
45449
|
+
systemCache = null;
|
|
45084
45450
|
saveBody = external_exports.object({
|
|
45085
45451
|
id: external_exports.string().optional(),
|
|
45086
45452
|
host: external_exports.string().min(1).max(253),
|
|
@@ -45094,6 +45460,113 @@ var init_certificates = __esm({
|
|
|
45094
45460
|
}
|
|
45095
45461
|
});
|
|
45096
45462
|
|
|
45463
|
+
// ../../apps/engine/src/cookies.ts
|
|
45464
|
+
function useCookieStore(secrets2) {
|
|
45465
|
+
store = secrets2;
|
|
45466
|
+
jar = null;
|
|
45467
|
+
}
|
|
45468
|
+
async function load() {
|
|
45469
|
+
if (jar) return jar;
|
|
45470
|
+
try {
|
|
45471
|
+
const raw = store ? await store.get(KEY) : null;
|
|
45472
|
+
jar = raw ? JSON.parse(raw).filter((c) => c.expires !== null) : [];
|
|
45473
|
+
} catch {
|
|
45474
|
+
jar = [];
|
|
45475
|
+
}
|
|
45476
|
+
return jar;
|
|
45477
|
+
}
|
|
45478
|
+
async function save(next) {
|
|
45479
|
+
const now = Date.now();
|
|
45480
|
+
jar = next.filter((c) => c.expires === null || c.expires > now).slice(-MAX);
|
|
45481
|
+
await store?.set(KEY, JSON.stringify(jar));
|
|
45482
|
+
}
|
|
45483
|
+
function defaultPath(url2) {
|
|
45484
|
+
const p = url2.pathname;
|
|
45485
|
+
if (!p.startsWith("/") || p.lastIndexOf("/") === 0) return "/";
|
|
45486
|
+
return p.slice(0, p.lastIndexOf("/"));
|
|
45487
|
+
}
|
|
45488
|
+
function parseSetCookie(header, url2, now = Date.now()) {
|
|
45489
|
+
const [pair, ...attrs] = header.split(";");
|
|
45490
|
+
const eq = pair.indexOf("=");
|
|
45491
|
+
if (eq <= 0) return null;
|
|
45492
|
+
const host2 = url2.hostname.toLowerCase();
|
|
45493
|
+
const cookie = { name: pair.slice(0, eq).trim(), value: pair.slice(eq + 1).trim(), domain: host2, includeSubdomains: false, path: defaultPath(url2), expires: null, secure: false, httpOnly: false };
|
|
45494
|
+
let maxAge = null;
|
|
45495
|
+
for (const raw of attrs) {
|
|
45496
|
+
const i = raw.indexOf("=");
|
|
45497
|
+
const k = (i < 0 ? raw : raw.slice(0, i)).trim().toLowerCase();
|
|
45498
|
+
const v = i < 0 ? "" : raw.slice(i + 1).trim();
|
|
45499
|
+
if (k === "domain" && v) {
|
|
45500
|
+
const d = v.replace(/^\./, "").toLowerCase();
|
|
45501
|
+
if (host2 !== d && !host2.endsWith(`.${d}`)) return null;
|
|
45502
|
+
cookie.domain = d;
|
|
45503
|
+
cookie.includeSubdomains = true;
|
|
45504
|
+
} else if (k === "path" && v.startsWith("/")) cookie.path = v;
|
|
45505
|
+
else if (k === "expires") {
|
|
45506
|
+
const t = Date.parse(v);
|
|
45507
|
+
if (Number.isFinite(t)) cookie.expires = t;
|
|
45508
|
+
} else if (k === "max-age" && /^-?\d+$/.test(v)) maxAge = Number(v);
|
|
45509
|
+
else if (k === "secure") cookie.secure = true;
|
|
45510
|
+
else if (k === "httponly") cookie.httpOnly = true;
|
|
45511
|
+
else if (k === "samesite") cookie.sameSite = v;
|
|
45512
|
+
}
|
|
45513
|
+
if (maxAge !== null) cookie.expires = now + maxAge * 1e3;
|
|
45514
|
+
return cookie;
|
|
45515
|
+
}
|
|
45516
|
+
function matches(c, url2, now) {
|
|
45517
|
+
const host2 = url2.hostname.toLowerCase();
|
|
45518
|
+
const domainOk = c.includeSubdomains ? host2 === c.domain || host2.endsWith(`.${c.domain}`) : host2 === c.domain;
|
|
45519
|
+
const path = url2.pathname || "/";
|
|
45520
|
+
const pathOk = path === c.path || path.startsWith(c.path) && (c.path.endsWith("/") || path[c.path.length] === "/");
|
|
45521
|
+
return domainOk && pathOk && (!c.secure || url2.protocol === "https:") && (c.expires === null || c.expires > now);
|
|
45522
|
+
}
|
|
45523
|
+
async function cookieHeader(target) {
|
|
45524
|
+
let url2;
|
|
45525
|
+
try {
|
|
45526
|
+
url2 = typeof target === "string" ? new URL(target) : target;
|
|
45527
|
+
} catch {
|
|
45528
|
+
return "";
|
|
45529
|
+
}
|
|
45530
|
+
const now = Date.now();
|
|
45531
|
+
return (await load()).filter((c) => matches(c, url2, now)).sort((a, b) => b.path.length - a.path.length).map((c) => `${c.name}=${c.value}`).join("; ");
|
|
45532
|
+
}
|
|
45533
|
+
async function storeCookies(target, setCookie) {
|
|
45534
|
+
if (!setCookie || Array.isArray(setCookie) && !setCookie.length) return;
|
|
45535
|
+
const url2 = typeof target === "string" ? new URL(target) : target;
|
|
45536
|
+
const now = Date.now();
|
|
45537
|
+
let next = [...await load()];
|
|
45538
|
+
for (const header of Array.isArray(setCookie) ? setCookie : [setCookie]) {
|
|
45539
|
+
const c = parseSetCookie(header, url2, now);
|
|
45540
|
+
if (!c) continue;
|
|
45541
|
+
next = next.filter((o) => !(o.name === c.name && o.domain === c.domain && o.path === c.path));
|
|
45542
|
+
if (c.expires === null || c.expires > now) next.push(c);
|
|
45543
|
+
}
|
|
45544
|
+
await save(next);
|
|
45545
|
+
}
|
|
45546
|
+
async function listCookies() {
|
|
45547
|
+
const now = Date.now();
|
|
45548
|
+
return (await load()).filter((c) => c.expires === null || c.expires > now);
|
|
45549
|
+
}
|
|
45550
|
+
function registerCookieRoutes(app2) {
|
|
45551
|
+
app2.get("/cookies", async () => (await listCookies()).map((c) => ({ ...c, value: c.value.length > 12 ? `${c.value.slice(0, 6)}\u2026${c.value.slice(-3)}` : c.value })));
|
|
45552
|
+
app2.post("/cookies/delete", async (request3) => {
|
|
45553
|
+
const b = request3.body ?? {};
|
|
45554
|
+
const list = await load();
|
|
45555
|
+
await save(b.all ? [] : b.domainOnly ? list.filter((c) => c.domain !== b.domainOnly) : list.filter((c) => !(c.name === b.name && c.domain === b.domain && c.path === b.path)));
|
|
45556
|
+
return { left: (await listCookies()).length };
|
|
45557
|
+
});
|
|
45558
|
+
}
|
|
45559
|
+
var KEY, MAX, store, jar;
|
|
45560
|
+
var init_cookies = __esm({
|
|
45561
|
+
"../../apps/engine/src/cookies.ts"() {
|
|
45562
|
+
"use strict";
|
|
45563
|
+
KEY = "cookies";
|
|
45564
|
+
MAX = 500;
|
|
45565
|
+
store = null;
|
|
45566
|
+
jar = null;
|
|
45567
|
+
}
|
|
45568
|
+
});
|
|
45569
|
+
|
|
45097
45570
|
// ../../apps/engine/src/jwt.ts
|
|
45098
45571
|
import { createHmac, createPrivateKey, sign as cryptoSign, constants as constants2 } from "node:crypto";
|
|
45099
45572
|
function privateKey(pem) {
|
|
@@ -45103,7 +45576,7 @@ function privateKey(pem) {
|
|
|
45103
45576
|
throw new Error(`the private key can't be read (${err instanceof Error ? err.message : "bad PEM"}) \u2014 paste the whole PEM, from -----BEGIN to -----END`);
|
|
45104
45577
|
}
|
|
45105
45578
|
}
|
|
45106
|
-
function signJwt(c, now = Math.floor(Date.now() / 1e3)) {
|
|
45579
|
+
function signJwt(c, now = Math.floor(Date.now() / 1e3), headerExtra = {}) {
|
|
45107
45580
|
let claims;
|
|
45108
45581
|
try {
|
|
45109
45582
|
claims = JSON.parse(c.payload || "{}");
|
|
@@ -45113,6 +45586,7 @@ function signJwt(c, now = Math.floor(Date.now() / 1e3)) {
|
|
|
45113
45586
|
const body = { iat: now, exp: now + c.lifetimeSec, ...claims };
|
|
45114
45587
|
const header = { alg: c.algorithm, typ: "JWT" };
|
|
45115
45588
|
if (c.kid) header.kid = c.kid;
|
|
45589
|
+
Object.assign(header, headerExtra);
|
|
45116
45590
|
const input = `${b64url(JSON.stringify(header))}.${b64url(JSON.stringify(body))}`;
|
|
45117
45591
|
const bits = c.algorithm.slice(2);
|
|
45118
45592
|
const hash2 = `sha${bits}`;
|
|
@@ -59388,13 +59862,13 @@ var require_mock_call_history = __commonJS({
|
|
|
59388
59862
|
"use strict";
|
|
59389
59863
|
var { kMockCallHistoryAddLog } = require_mock_symbols();
|
|
59390
59864
|
var { InvalidArgumentError } = require_errors2();
|
|
59391
|
-
function handleFilterCallsWithOptions(criteria, options, handler,
|
|
59865
|
+
function handleFilterCallsWithOptions(criteria, options, handler, store3, allLogs) {
|
|
59392
59866
|
switch (options.operator) {
|
|
59393
59867
|
case "OR":
|
|
59394
|
-
|
|
59395
|
-
return
|
|
59868
|
+
store3.push(...handler(criteria, allLogs));
|
|
59869
|
+
return store3;
|
|
59396
59870
|
case "AND":
|
|
59397
|
-
return handler(criteria,
|
|
59871
|
+
return handler(criteria, store3);
|
|
59398
59872
|
default:
|
|
59399
59873
|
throw new InvalidArgumentError("options.operator must to be a case insensitive string equal to 'OR' or 'AND'");
|
|
59400
59874
|
}
|
|
@@ -62049,12 +62523,12 @@ var require_cache = __commonJS({
|
|
|
62049
62523
|
}
|
|
62050
62524
|
return false;
|
|
62051
62525
|
}
|
|
62052
|
-
function assertCacheStore(
|
|
62053
|
-
if (typeof
|
|
62054
|
-
throw new TypeError(`expected type of ${name} to be a CacheStore, got ${
|
|
62526
|
+
function assertCacheStore(store3, name = "CacheStore") {
|
|
62527
|
+
if (typeof store3 !== "object" || store3 === null) {
|
|
62528
|
+
throw new TypeError(`expected type of ${name} to be a CacheStore, got ${store3 === null ? "null" : typeof store3}`);
|
|
62055
62529
|
}
|
|
62056
62530
|
for (const fn of ["get", "createWriteStream", "delete"]) {
|
|
62057
|
-
if (typeof
|
|
62531
|
+
if (typeof store3[fn] !== "function") {
|
|
62058
62532
|
throw new TypeError(`${name} needs to have a \`${fn}()\` function`);
|
|
62059
62533
|
}
|
|
62060
62534
|
}
|
|
@@ -62677,15 +63151,15 @@ var require_cache_handler = __commonJS({
|
|
|
62677
63151
|
}
|
|
62678
63152
|
return locationUrl.pathname + locationUrl.search;
|
|
62679
63153
|
}
|
|
62680
|
-
function deleteCachedUri(
|
|
62681
|
-
deleteCachedValue(
|
|
63154
|
+
function deleteCachedUri(store3, cacheKey, path) {
|
|
63155
|
+
deleteCachedValue(store3, {
|
|
62682
63156
|
...cacheKey,
|
|
62683
63157
|
path
|
|
62684
63158
|
});
|
|
62685
63159
|
for (let i = 0; i < util.safeHTTPMethods.length; i++) {
|
|
62686
63160
|
const method = util.safeHTTPMethods[i];
|
|
62687
63161
|
if (method !== cacheKey.method) {
|
|
62688
|
-
deleteCachedValue(
|
|
63162
|
+
deleteCachedValue(store3, {
|
|
62689
63163
|
...cacheKey,
|
|
62690
63164
|
method,
|
|
62691
63165
|
path
|
|
@@ -62693,7 +63167,7 @@ var require_cache_handler = __commonJS({
|
|
|
62693
63167
|
}
|
|
62694
63168
|
}
|
|
62695
63169
|
}
|
|
62696
|
-
function deleteLocationTargets(
|
|
63170
|
+
function deleteLocationTargets(store3, cacheKey, headerValue) {
|
|
62697
63171
|
if (headerValue === void 0) {
|
|
62698
63172
|
return;
|
|
62699
63173
|
}
|
|
@@ -62701,14 +63175,14 @@ var require_cache_handler = __commonJS({
|
|
|
62701
63175
|
for (let i = 0; i < values.length; i++) {
|
|
62702
63176
|
const path = getSameOriginPath(cacheKey, values[i]);
|
|
62703
63177
|
if (path !== void 0) {
|
|
62704
|
-
deleteCachedUri(
|
|
63178
|
+
deleteCachedUri(store3, cacheKey, path);
|
|
62705
63179
|
}
|
|
62706
63180
|
}
|
|
62707
63181
|
}
|
|
62708
|
-
function invalidateUnsafeRequest(
|
|
62709
|
-
deleteCachedUri(
|
|
62710
|
-
deleteLocationTargets(
|
|
62711
|
-
deleteLocationTargets(
|
|
63182
|
+
function invalidateUnsafeRequest(store3, cacheKey, resHeaders) {
|
|
63183
|
+
deleteCachedUri(store3, cacheKey, cacheKey.path);
|
|
63184
|
+
deleteLocationTargets(store3, cacheKey, resHeaders.location);
|
|
63185
|
+
deleteLocationTargets(store3, cacheKey, resHeaders["content-location"]);
|
|
62712
63186
|
}
|
|
62713
63187
|
var CacheHandler = class {
|
|
62714
63188
|
/**
|
|
@@ -62740,8 +63214,8 @@ var require_cache_handler = __commonJS({
|
|
|
62740
63214
|
* @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} cacheKey
|
|
62741
63215
|
* @param {import('../../types/dispatcher.d.ts').default.DispatchHandler} handler
|
|
62742
63216
|
*/
|
|
62743
|
-
constructor({ store:
|
|
62744
|
-
this.#store =
|
|
63217
|
+
constructor({ store: store3, type, cacheByDefault }, cacheKey, handler) {
|
|
63218
|
+
this.#store = store3;
|
|
62745
63219
|
this.#cacheType = type;
|
|
62746
63220
|
this.#cacheByDefault = cacheByDefault;
|
|
62747
63221
|
this.#cacheKey = cacheKey;
|
|
@@ -62935,15 +63409,15 @@ var require_cache_handler = __commonJS({
|
|
|
62935
63409
|
this.#handler.onResponseError?.(controller, err);
|
|
62936
63410
|
}
|
|
62937
63411
|
};
|
|
62938
|
-
function deleteCachedValue(
|
|
63412
|
+
function deleteCachedValue(store3, cacheKey) {
|
|
62939
63413
|
try {
|
|
62940
|
-
|
|
63414
|
+
store3.delete(cacheKey)?.catch?.(noop);
|
|
62941
63415
|
} catch {
|
|
62942
63416
|
}
|
|
62943
63417
|
}
|
|
62944
|
-
function deleteCachedValueIfNotModified(statusCode,
|
|
63418
|
+
function deleteCachedValueIfNotModified(statusCode, store3, cacheKey) {
|
|
62945
63419
|
if (statusCode === 304) {
|
|
62946
|
-
deleteCachedValue(
|
|
63420
|
+
deleteCachedValue(store3, cacheKey);
|
|
62947
63421
|
}
|
|
62948
63422
|
}
|
|
62949
63423
|
function revalidationResponseDisallowsCachedReuse(cacheType, resHeaders, cacheControlDirectives) {
|
|
@@ -63226,7 +63700,7 @@ var require_memory_cache_store = __commonJS({
|
|
|
63226
63700
|
assertCacheKey(key);
|
|
63227
63701
|
assertCacheValue(val);
|
|
63228
63702
|
const topLevelKey = `${key.origin}:${key.path}`;
|
|
63229
|
-
const
|
|
63703
|
+
const store3 = this;
|
|
63230
63704
|
const entry = { ...key, ...val, body: [], size: 0 };
|
|
63231
63705
|
return new Writable({
|
|
63232
63706
|
write(chunk, encoding, callback) {
|
|
@@ -63234,7 +63708,7 @@ var require_memory_cache_store = __commonJS({
|
|
|
63234
63708
|
chunk = Buffer.from(chunk, encoding);
|
|
63235
63709
|
}
|
|
63236
63710
|
entry.size += chunk.byteLength;
|
|
63237
|
-
if (entry.size >
|
|
63711
|
+
if (entry.size > store3.#maxEntrySize) {
|
|
63238
63712
|
this.destroy();
|
|
63239
63713
|
} else {
|
|
63240
63714
|
entry.body.push(chunk);
|
|
@@ -63242,42 +63716,42 @@ var require_memory_cache_store = __commonJS({
|
|
|
63242
63716
|
callback(null);
|
|
63243
63717
|
},
|
|
63244
63718
|
final(callback) {
|
|
63245
|
-
let entries =
|
|
63719
|
+
let entries = store3.#entries.get(topLevelKey);
|
|
63246
63720
|
if (!entries) {
|
|
63247
63721
|
entries = [];
|
|
63248
|
-
|
|
63722
|
+
store3.#entries.set(topLevelKey, entries);
|
|
63249
63723
|
}
|
|
63250
63724
|
const previousEntry = findEntry(key, entries, Date.now());
|
|
63251
63725
|
if (previousEntry) {
|
|
63252
63726
|
const index = entries.indexOf(previousEntry);
|
|
63253
63727
|
entries.splice(index, 1, entry);
|
|
63254
|
-
|
|
63728
|
+
store3.#size -= previousEntry.size;
|
|
63255
63729
|
} else {
|
|
63256
63730
|
entries.push(entry);
|
|
63257
|
-
|
|
63258
|
-
}
|
|
63259
|
-
|
|
63260
|
-
if (
|
|
63261
|
-
if (!
|
|
63262
|
-
|
|
63263
|
-
size:
|
|
63264
|
-
maxSize:
|
|
63265
|
-
count:
|
|
63266
|
-
maxCount:
|
|
63731
|
+
store3.#count += 1;
|
|
63732
|
+
}
|
|
63733
|
+
store3.#size += entry.size;
|
|
63734
|
+
if (store3.#size > store3.#maxSize || store3.#count > store3.#maxCount) {
|
|
63735
|
+
if (!store3.#hasEmittedMaxSizeEvent) {
|
|
63736
|
+
store3.emit("maxSizeExceeded", {
|
|
63737
|
+
size: store3.#size,
|
|
63738
|
+
maxSize: store3.#maxSize,
|
|
63739
|
+
count: store3.#count,
|
|
63740
|
+
maxCount: store3.#maxCount
|
|
63267
63741
|
});
|
|
63268
|
-
|
|
63742
|
+
store3.#hasEmittedMaxSizeEvent = true;
|
|
63269
63743
|
}
|
|
63270
|
-
for (const [key2, entries2] of
|
|
63744
|
+
for (const [key2, entries2] of store3.#entries) {
|
|
63271
63745
|
for (const entry2 of entries2.splice(0, entries2.length / 2)) {
|
|
63272
|
-
|
|
63273
|
-
|
|
63746
|
+
store3.#size -= entry2.size;
|
|
63747
|
+
store3.#count -= 1;
|
|
63274
63748
|
}
|
|
63275
63749
|
if (entries2.length === 0) {
|
|
63276
|
-
|
|
63750
|
+
store3.#entries.delete(key2);
|
|
63277
63751
|
}
|
|
63278
63752
|
}
|
|
63279
|
-
if (
|
|
63280
|
-
|
|
63753
|
+
if (store3.#size < store3.#maxSize && store3.#count < store3.#maxCount) {
|
|
63754
|
+
store3.#hasEmittedMaxSizeEvent = false;
|
|
63281
63755
|
}
|
|
63282
63756
|
}
|
|
63283
63757
|
callback(null);
|
|
@@ -63524,9 +63998,9 @@ var require_cache2 = __commonJS({
|
|
|
63524
63998
|
function revalidationResponseUpdatesCacheControl(headers) {
|
|
63525
63999
|
return headers["cache-control"] !== void 0;
|
|
63526
64000
|
}
|
|
63527
|
-
function deleteCachedValue(
|
|
64001
|
+
function deleteCachedValue(store3, cacheKey) {
|
|
63528
64002
|
try {
|
|
63529
|
-
|
|
64003
|
+
store3.delete(cacheKey)?.catch?.(nop);
|
|
63530
64004
|
} catch {
|
|
63531
64005
|
}
|
|
63532
64006
|
}
|
|
@@ -63761,7 +64235,7 @@ var require_cache2 = __commonJS({
|
|
|
63761
64235
|
}
|
|
63762
64236
|
module.exports = (opts = {}) => {
|
|
63763
64237
|
const {
|
|
63764
|
-
store:
|
|
64238
|
+
store: store3 = new MemoryCacheStore(),
|
|
63765
64239
|
methods = ["GET"],
|
|
63766
64240
|
cacheByDefault = void 0,
|
|
63767
64241
|
type = "shared",
|
|
@@ -63770,7 +64244,7 @@ var require_cache2 = __commonJS({
|
|
|
63770
64244
|
if (typeof opts !== "object" || opts === null) {
|
|
63771
64245
|
throw new TypeError(`expected type of opts to be an Object, got ${opts === null ? "null" : typeof opts}`);
|
|
63772
64246
|
}
|
|
63773
|
-
assertCacheStore(
|
|
64247
|
+
assertCacheStore(store3, "opts.store");
|
|
63774
64248
|
assertCacheMethods(methods, "opts.methods");
|
|
63775
64249
|
assertCacheOrigins(origins, "opts.origins");
|
|
63776
64250
|
if (typeof cacheByDefault !== "undefined" && typeof cacheByDefault !== "number") {
|
|
@@ -63780,7 +64254,7 @@ var require_cache2 = __commonJS({
|
|
|
63780
64254
|
throw new TypeError(`expected opts.type to be shared, private, or undefined, got ${typeof type}`);
|
|
63781
64255
|
}
|
|
63782
64256
|
const globalOpts = {
|
|
63783
|
-
store:
|
|
64257
|
+
store: store3,
|
|
63784
64258
|
methods,
|
|
63785
64259
|
cacheByDefault,
|
|
63786
64260
|
type
|
|
@@ -63828,7 +64302,7 @@ var require_cache2 = __commonJS({
|
|
|
63828
64302
|
return dispatch(opts2, handler);
|
|
63829
64303
|
}
|
|
63830
64304
|
const cacheKey = makeCacheKey(opts2);
|
|
63831
|
-
const result =
|
|
64305
|
+
const result = store3.get(cacheKey);
|
|
63832
64306
|
if (result && typeof result.then === "function") {
|
|
63833
64307
|
return result.then((result2) => handleResult(
|
|
63834
64308
|
dispatch,
|
|
@@ -64791,12 +65265,12 @@ var require_sqlite_cache_store = __commonJS({
|
|
|
64791
65265
|
assertCacheValue(value);
|
|
64792
65266
|
let size = 0;
|
|
64793
65267
|
const body = [];
|
|
64794
|
-
const
|
|
65268
|
+
const store3 = this;
|
|
64795
65269
|
return new Writable({
|
|
64796
65270
|
decodeStrings: true,
|
|
64797
65271
|
write(chunk, encoding, callback) {
|
|
64798
65272
|
size += chunk.byteLength;
|
|
64799
|
-
if (size <=
|
|
65273
|
+
if (size <= store3.#maxEntrySize) {
|
|
64800
65274
|
body.push(chunk);
|
|
64801
65275
|
} else {
|
|
64802
65276
|
this.destroy();
|
|
@@ -64804,7 +65278,7 @@ var require_sqlite_cache_store = __commonJS({
|
|
|
64804
65278
|
callback();
|
|
64805
65279
|
},
|
|
64806
65280
|
final(callback) {
|
|
64807
|
-
|
|
65281
|
+
store3.set(key, { ...value, body });
|
|
64808
65282
|
callback();
|
|
64809
65283
|
}
|
|
64810
65284
|
});
|
|
@@ -64868,17 +65342,17 @@ var require_sqlite_cache_store = __commonJS({
|
|
|
64868
65342
|
if (now >= value.deleteAt && !canBeExpired) {
|
|
64869
65343
|
continue;
|
|
64870
65344
|
}
|
|
64871
|
-
let
|
|
65345
|
+
let matches2 = true;
|
|
64872
65346
|
if (value.vary) {
|
|
64873
65347
|
const vary = JSON.parse(value.vary);
|
|
64874
65348
|
for (const header in vary) {
|
|
64875
65349
|
if (!headerValueEquals(headers[header], vary[header])) {
|
|
64876
|
-
|
|
65350
|
+
matches2 = false;
|
|
64877
65351
|
break;
|
|
64878
65352
|
}
|
|
64879
65353
|
}
|
|
64880
65354
|
}
|
|
64881
|
-
if (
|
|
65355
|
+
if (matches2) {
|
|
64882
65356
|
return value;
|
|
64883
65357
|
}
|
|
64884
65358
|
}
|
|
@@ -68771,7 +69245,7 @@ var require_parse = __commonJS({
|
|
|
68771
69245
|
var { maxNameValuePairSize, maxAttributeValueSize } = require_constants4();
|
|
68772
69246
|
var { isCTLExcludingHtab } = require_util5();
|
|
68773
69247
|
var assert2 = __require("node:assert");
|
|
68774
|
-
function
|
|
69248
|
+
function parseSetCookie2(header) {
|
|
68775
69249
|
if (isCTLExcludingHtab(header)) {
|
|
68776
69250
|
return null;
|
|
68777
69251
|
}
|
|
@@ -68895,7 +69369,7 @@ var require_parse = __commonJS({
|
|
|
68895
69369
|
return parseUnparsedAttributes(unparsedAttributes, cookieAttributeList);
|
|
68896
69370
|
}
|
|
68897
69371
|
module.exports = {
|
|
68898
|
-
parseSetCookie,
|
|
69372
|
+
parseSetCookie: parseSetCookie2,
|
|
68899
69373
|
parseUnparsedAttributes
|
|
68900
69374
|
};
|
|
68901
69375
|
}
|
|
@@ -68905,7 +69379,7 @@ var require_parse = __commonJS({
|
|
|
68905
69379
|
var require_cookies = __commonJS({
|
|
68906
69380
|
"../../node_modules/.pnpm/undici@8.10.0/node_modules/undici/lib/web/cookies/index.js"(exports, module) {
|
|
68907
69381
|
"use strict";
|
|
68908
|
-
var { parseSetCookie } = require_parse();
|
|
69382
|
+
var { parseSetCookie: parseSetCookie2 } = require_parse();
|
|
68909
69383
|
var { stringify } = require_util5();
|
|
68910
69384
|
var { webidl } = require_webidl();
|
|
68911
69385
|
var { Headers } = require_headers();
|
|
@@ -68944,11 +69418,11 @@ var require_cookies = __commonJS({
|
|
|
68944
69418
|
if (!cookies) {
|
|
68945
69419
|
return [];
|
|
68946
69420
|
}
|
|
68947
|
-
return cookies.map((pair) =>
|
|
69421
|
+
return cookies.map((pair) => parseSetCookie2(pair));
|
|
68948
69422
|
}
|
|
68949
69423
|
function parseCookie(cookie) {
|
|
68950
69424
|
cookie = webidl.converters.DOMString(cookie);
|
|
68951
|
-
return
|
|
69425
|
+
return parseSetCookie2(cookie);
|
|
68952
69426
|
}
|
|
68953
69427
|
function setCookie(headers, cookie) {
|
|
68954
69428
|
webidl.argumentLengthCheck(arguments, 2, "setCookie");
|
|
@@ -72104,19 +72578,19 @@ ${captureLines}` : capture.stack;
|
|
|
72104
72578
|
});
|
|
72105
72579
|
|
|
72106
72580
|
// ../../apps/engine/src/oauth.ts
|
|
72107
|
-
import { createHash as createHash2, randomBytes, randomUUID as randomUUID3 } from "node:crypto";
|
|
72581
|
+
import { X509Certificate as X509Certificate2, createHash as createHash2, randomBytes, randomUUID as randomUUID3 } from "node:crypto";
|
|
72108
72582
|
import { createServer } from "node:http";
|
|
72109
72583
|
function useOAuthStore(secrets2) {
|
|
72110
|
-
|
|
72584
|
+
store2 = secrets2;
|
|
72111
72585
|
}
|
|
72112
72586
|
function oauthKey(c) {
|
|
72113
72587
|
const id = JSON.stringify([c.grant, c.tokenUrl, c.authUrl, c.clientId, c.clientSecret, c.scope, c.username, c.password, [...c.extra].map((e) => `${e.key}=${e.value}`).sort()]);
|
|
72114
72588
|
return createHash2("sha256").update(id).digest("hex").slice(0, 32);
|
|
72115
72589
|
}
|
|
72116
|
-
async function
|
|
72590
|
+
async function load2(key) {
|
|
72117
72591
|
const hit = memory.get(key);
|
|
72118
72592
|
if (hit) return hit;
|
|
72119
|
-
const raw = await
|
|
72593
|
+
const raw = await store2?.get(`oauth:${key}`);
|
|
72120
72594
|
if (!raw) return null;
|
|
72121
72595
|
try {
|
|
72122
72596
|
const token = JSON.parse(raw);
|
|
@@ -72126,21 +72600,54 @@ async function load(key) {
|
|
|
72126
72600
|
return null;
|
|
72127
72601
|
}
|
|
72128
72602
|
}
|
|
72129
|
-
async function
|
|
72603
|
+
async function save2(key, token) {
|
|
72130
72604
|
memory.set(key, token);
|
|
72131
|
-
await
|
|
72605
|
+
await store2?.set(`oauth:${key}`, JSON.stringify(token));
|
|
72132
72606
|
}
|
|
72133
72607
|
async function clearToken(c) {
|
|
72134
72608
|
const key = oauthKey(c);
|
|
72135
72609
|
memory.delete(key);
|
|
72136
|
-
await
|
|
72610
|
+
await store2?.set(`oauth:${key}`, "");
|
|
72137
72611
|
}
|
|
72138
|
-
function
|
|
72612
|
+
function thumbprint(pem) {
|
|
72613
|
+
if (!pem.trim()) return void 0;
|
|
72614
|
+
try {
|
|
72615
|
+
return Buffer.from(new X509Certificate2(pem).fingerprint.replace(/:/g, ""), "hex").toString("base64url");
|
|
72616
|
+
} catch {
|
|
72617
|
+
throw new OAuthError("the certificate can't be read \u2014 paste the whole PEM, from -----BEGIN CERTIFICATE----- to -----END CERTIFICATE-----");
|
|
72618
|
+
}
|
|
72619
|
+
}
|
|
72620
|
+
function signedAssertion(c, claims, now) {
|
|
72621
|
+
const x5t = thumbprint(c.certificate);
|
|
72622
|
+
try {
|
|
72623
|
+
return signJwt(
|
|
72624
|
+
{ algorithm: c.assertionAlgorithm, key: c.privateKey, payload: JSON.stringify({ jti: randomUUID3(), ...claims }), lifetimeSec: 300, kid: c.kid, headerName: "", prefix: "" },
|
|
72625
|
+
now,
|
|
72626
|
+
x5t ? { x5t } : {}
|
|
72627
|
+
);
|
|
72628
|
+
} catch (err) {
|
|
72629
|
+
throw new OAuthError(err instanceof Error ? err.message : String(err));
|
|
72630
|
+
}
|
|
72631
|
+
}
|
|
72632
|
+
function extraClaims(c) {
|
|
72633
|
+
if (!c.extraClaims) return {};
|
|
72634
|
+
try {
|
|
72635
|
+
return JSON.parse(c.extraClaims);
|
|
72636
|
+
} catch {
|
|
72637
|
+
throw new OAuthError("the extra claims are not valid JSON");
|
|
72638
|
+
}
|
|
72639
|
+
}
|
|
72640
|
+
function tokenRequest(c, fields, now = Math.floor(Date.now() / 1e3)) {
|
|
72139
72641
|
const form = new URLSearchParams(fields);
|
|
72140
72642
|
if (c.scope && !form.has("scope") && fields.grant_type !== "authorization_code") form.set("scope", c.scope);
|
|
72141
72643
|
for (const e of c.extra) if (e.key.trim()) form.set(e.key.trim(), e.value);
|
|
72142
72644
|
const headers = { "content-type": "application/x-www-form-urlencoded", accept: "application/json" };
|
|
72143
|
-
if (c.clientAuth === "
|
|
72645
|
+
if (c.clientAuth === "private_key_jwt") {
|
|
72646
|
+
form.set("client_id", c.clientId);
|
|
72647
|
+
form.set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer");
|
|
72648
|
+
form.set("client_assertion", signedAssertion(c, { iss: c.clientId, sub: c.clientId, aud: c.audience || c.tokenUrl }, now));
|
|
72649
|
+
} else if (fields.grant_type === JWT_BEARER && !c.clientSecret) {
|
|
72650
|
+
} else if (c.clientAuth === "basic" && c.clientSecret) {
|
|
72144
72651
|
headers.authorization = `Basic ${Buffer.from(`${encodeURIComponent(c.clientId)}:${encodeURIComponent(c.clientSecret)}`).toString("base64")}`;
|
|
72145
72652
|
} else {
|
|
72146
72653
|
form.set("client_id", c.clientId);
|
|
@@ -72182,39 +72689,84 @@ async function postToken(c, fields) {
|
|
|
72182
72689
|
obtainedAt: Date.now()
|
|
72183
72690
|
};
|
|
72184
72691
|
}
|
|
72185
|
-
function grantFields(c) {
|
|
72692
|
+
async function grantFields(c, now = Math.floor(Date.now() / 1e3)) {
|
|
72186
72693
|
if (c.grant === "client_credentials") return { grant_type: "client_credentials" };
|
|
72187
72694
|
if (c.grant === "password") return { grant_type: "password", username: c.username, password: c.password };
|
|
72695
|
+
if (c.grant === "jwt_bearer") {
|
|
72696
|
+
const claims = { iss: c.issuer || c.clientId, ...c.subject ? { sub: c.subject } : {}, aud: c.audience || c.tokenUrl, ...extraClaims(c) };
|
|
72697
|
+
return { grant_type: JWT_BEARER, assertion: signedAssertion(c, claims, now) };
|
|
72698
|
+
}
|
|
72699
|
+
if (c.grant === "saml_bearer") {
|
|
72700
|
+
const fields = { grant_type: SAML_BEARER, assertion: c.samlSource === "assertion" ? c.assertion : await samlFromIdp(c) };
|
|
72701
|
+
if (c.companyId) fields.company_id = c.companyId;
|
|
72702
|
+
return fields;
|
|
72703
|
+
}
|
|
72188
72704
|
throw new OAuthError("sign in first \u2014 press \u201CSign in in the browser\u201D on the Auth tab; the token is then kept and refreshed");
|
|
72189
72705
|
}
|
|
72706
|
+
async function samlFromIdp(c) {
|
|
72707
|
+
const key = c.privateKey.replace(/-----[^-]+-----/g, "").replace(/\s+/g, "");
|
|
72708
|
+
const form = new URLSearchParams({ client_id: c.clientId, user_id: c.userId, token_url: c.tokenUrl, private_key: key });
|
|
72709
|
+
if (c.companyId) form.set("company_id", c.companyId);
|
|
72710
|
+
let status;
|
|
72711
|
+
let text;
|
|
72712
|
+
try {
|
|
72713
|
+
const connect = await tlsConnect(c.idpUrl);
|
|
72714
|
+
const res = await (0, import_undici.request)(c.idpUrl, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form.toString(), headersTimeout: 2e4, bodyTimeout: 2e4, dispatcher: connect ? new import_undici.Agent({ connect }) : void 0 });
|
|
72715
|
+
status = res.statusCode;
|
|
72716
|
+
text = (await res.body.text()).trim();
|
|
72717
|
+
} catch (err) {
|
|
72718
|
+
throw new OAuthError(`couldn't reach the assertion URL (${err instanceof Error ? err.message : "network error"})`);
|
|
72719
|
+
}
|
|
72720
|
+
if (status < 200 || status >= 300 || !text || text.startsWith("{") || text.startsWith("<")) {
|
|
72721
|
+
throw new OAuthError(`the assertion URL answered ${status} (${text.slice(0, 160) || "no details"}) \u2014 check the client ID, user ID and private key`);
|
|
72722
|
+
}
|
|
72723
|
+
return text;
|
|
72724
|
+
}
|
|
72190
72725
|
async function getToken(c, opts = {}) {
|
|
72191
72726
|
const key = oauthKey(c);
|
|
72192
|
-
const kept = await
|
|
72727
|
+
const kept = await load2(key);
|
|
72193
72728
|
if (!opts.force && fresh(kept)) return kept;
|
|
72194
72729
|
if (kept?.refreshToken) {
|
|
72195
72730
|
try {
|
|
72196
72731
|
const next = await postToken(c, { grant_type: "refresh_token", refresh_token: kept.refreshToken });
|
|
72197
72732
|
const token2 = { ...next, refreshToken: next.refreshToken ?? kept.refreshToken };
|
|
72198
|
-
await
|
|
72733
|
+
await save2(key, token2);
|
|
72199
72734
|
return token2;
|
|
72200
72735
|
} catch (err) {
|
|
72201
72736
|
if (c.grant === "authorization_code") throw new OAuthError(`the refresh token stopped working (${err instanceof Error ? err.message : "error"}) \u2014 sign in in the browser again`);
|
|
72202
72737
|
}
|
|
72203
72738
|
}
|
|
72204
|
-
const token = await postToken(c, grantFields(c));
|
|
72205
|
-
await
|
|
72739
|
+
const token = await postToken(c, await grantFields(c));
|
|
72740
|
+
await save2(key, token);
|
|
72206
72741
|
return token;
|
|
72207
72742
|
}
|
|
72208
72743
|
async function tokenStatus(c) {
|
|
72209
|
-
const t = await
|
|
72744
|
+
const t = await load2(oauthKey(c));
|
|
72210
72745
|
if (!t?.accessToken) return { hasToken: false, expiresAt: null, canRefresh: c.grant !== "authorization_code" };
|
|
72211
72746
|
return { hasToken: true, expiresAt: t.expiresAt, scope: t.scope, canRefresh: Boolean(t.refreshToken) || c.grant !== "authorization_code", preview: `${t.accessToken.slice(0, 6)}\u2026` };
|
|
72212
72747
|
}
|
|
72213
|
-
function refreshPlan(c, t) {
|
|
72748
|
+
async function refreshPlan(c, t, runMs = 36e5) {
|
|
72214
72749
|
if (t.expiresAt === null) return null;
|
|
72215
|
-
|
|
72216
|
-
|
|
72217
|
-
|
|
72750
|
+
if (c.grant === "authorization_code") {
|
|
72751
|
+
return t.refreshToken ? { tokenUrl: c.tokenUrl, ...tokenRequest(c, { grant_type: "refresh_token", refresh_token: t.refreshToken }), expiresAt: t.expiresAt } : null;
|
|
72752
|
+
}
|
|
72753
|
+
if (c.grant === "saml_bearer" && c.samlSource === "idp") return null;
|
|
72754
|
+
const signs = c.grant === "jwt_bearer" || c.clientAuth === "private_key_jwt";
|
|
72755
|
+
if (!signs) {
|
|
72756
|
+
return { tokenUrl: c.tokenUrl, ...tokenRequest(c, await grantFields(c)), expiresAt: t.expiresAt };
|
|
72757
|
+
}
|
|
72758
|
+
const start = Math.floor(Date.now() / 1e3);
|
|
72759
|
+
const stepSec = 240;
|
|
72760
|
+
const count = Math.min(2e3, Math.ceil((runMs / 1e3 + 120) / stepSec));
|
|
72761
|
+
const list = [];
|
|
72762
|
+
let headers = {};
|
|
72763
|
+
for (let i = 0; i < count; i++) {
|
|
72764
|
+
const now = start + i * stepSec;
|
|
72765
|
+
const req = tokenRequest(c, await grantFields(c, now), now);
|
|
72766
|
+
headers = req.headers;
|
|
72767
|
+
list.push(req.body);
|
|
72768
|
+
}
|
|
72769
|
+
return { tokenUrl: c.tokenUrl, headers, body: list[0], expiresAt: t.expiresAt, bodies: { start, stepSec, list } };
|
|
72218
72770
|
}
|
|
72219
72771
|
async function startAuthorization(c) {
|
|
72220
72772
|
const redirect = new URL(c.redirectUri);
|
|
@@ -72257,7 +72809,7 @@ async function startAuthorization(c) {
|
|
|
72257
72809
|
if (!code) return finish(false, "No code came back.");
|
|
72258
72810
|
const fields = { grant_type: "authorization_code", code, redirect_uri: c.redirectUri };
|
|
72259
72811
|
if (c.pkce) fields.code_verifier = verifier;
|
|
72260
|
-
postToken(c, fields).then((token) =>
|
|
72812
|
+
postToken(c, fields).then((token) => save2(oauthKey(c), token)).then(() => finish(true, "You can close this tab and go back to APITestCenter.")).catch((err) => finish(false, err instanceof Error ? `Couldn't get the token: ${err.message}` : "Couldn't get the token."));
|
|
72261
72813
|
});
|
|
72262
72814
|
await new Promise((resolve5, reject) => {
|
|
72263
72815
|
server.once(
|
|
@@ -72319,14 +72871,15 @@ function registerOAuthRoutes(app2) {
|
|
|
72319
72871
|
});
|
|
72320
72872
|
app2.get("/oauth/authorize/:id", async (request3) => authorizationStatus(request3.params.id));
|
|
72321
72873
|
}
|
|
72322
|
-
var import_undici,
|
|
72874
|
+
var import_undici, store2, memory, OAuthError, HINTS, JWT_BEARER, SAML_BEARER, fresh, pending, page, b64url2;
|
|
72323
72875
|
var init_oauth = __esm({
|
|
72324
72876
|
"../../apps/engine/src/oauth.ts"() {
|
|
72325
72877
|
"use strict";
|
|
72326
72878
|
init_src();
|
|
72327
72879
|
import_undici = __toESM(require_undici(), 1);
|
|
72328
72880
|
init_certificates();
|
|
72329
|
-
|
|
72881
|
+
init_jwt();
|
|
72882
|
+
store2 = null;
|
|
72330
72883
|
memory = /* @__PURE__ */ new Map();
|
|
72331
72884
|
OAuthError = class extends Error {
|
|
72332
72885
|
};
|
|
@@ -72337,6 +72890,8 @@ var init_oauth = __esm({
|
|
|
72337
72890
|
invalid_scope: "the scope isn't allowed for this client",
|
|
72338
72891
|
unsupported_grant_type: "the server doesn't offer this grant type"
|
|
72339
72892
|
};
|
|
72893
|
+
JWT_BEARER = "urn:ietf:params:oauth:grant-type:jwt-bearer";
|
|
72894
|
+
SAML_BEARER = "urn:ietf:params:oauth:grant-type:saml2-bearer";
|
|
72340
72895
|
fresh = (t, marginMs = 6e4) => Boolean(t && (t.expiresAt === null || t.expiresAt - marginMs > Date.now()));
|
|
72341
72896
|
pending = /* @__PURE__ */ new Map();
|
|
72342
72897
|
page = (title, text) => `<!doctype html><meta charset="utf-8"><title>${title}</title><body style="font-family:system-ui;background:#0b0f14;color:#e6edf3;display:grid;place-items:center;height:100vh;margin:0"><div style="max-width:28rem;text-align:center"><h1 style="font-size:1.3rem">${title}</h1><p style="color:#9aa7b4">${text}</p></div>`;
|
|
@@ -72518,6 +73073,30 @@ var init_aws_sign = __esm({
|
|
|
72518
73073
|
}
|
|
72519
73074
|
});
|
|
72520
73075
|
|
|
73076
|
+
// ../../apps/engine/src/oauth1-sign.ts
|
|
73077
|
+
import { createHmac as createHmac4, randomBytes as randomBytes3 } from "node:crypto";
|
|
73078
|
+
function signOAuth1(c, req, nonce = randomBytes3(16).toString("hex"), timestamp = Math.floor(Date.now() / 1e3)) {
|
|
73079
|
+
const oauth = oauth1Params(c, nonce, timestamp);
|
|
73080
|
+
const query = /\?([^#]*)/.exec(req.url)?.[1] ?? "";
|
|
73081
|
+
const params = [...oauth, ...formPairs(query)];
|
|
73082
|
+
if (req.body && /^application\/x-www-form-urlencoded/i.test(req.contentType ?? "")) params.push(...formPairs(req.body));
|
|
73083
|
+
const baseString = oauth1BaseString(req.method, req.url, params);
|
|
73084
|
+
const key = oauth1SigningKey(c);
|
|
73085
|
+
const signature = c.signatureMethod === "PLAINTEXT" ? key : createHmac4(c.signatureMethod === "HMAC-SHA1" ? "sha1" : "sha256", key).update(baseString).digest("base64");
|
|
73086
|
+
const signed = [...oauth, ["oauth_signature", signature]];
|
|
73087
|
+
if (c.addTo === "query") {
|
|
73088
|
+
const extra2 = signed.map(([k, v]) => `${pctEncode(k)}=${pctEncode(v)}`).join("&");
|
|
73089
|
+
return { url: `${req.url}${req.url.includes("?") ? "&" : "?"}${extra2}`, signature, baseString };
|
|
73090
|
+
}
|
|
73091
|
+
return { url: req.url, authorization: oauth1Header(c, signed), signature, baseString };
|
|
73092
|
+
}
|
|
73093
|
+
var init_oauth1_sign = __esm({
|
|
73094
|
+
"../../apps/engine/src/oauth1-sign.ts"() {
|
|
73095
|
+
"use strict";
|
|
73096
|
+
init_src();
|
|
73097
|
+
}
|
|
73098
|
+
});
|
|
73099
|
+
|
|
72521
73100
|
// ../../apps/engine/src/probe.ts
|
|
72522
73101
|
function redactHeaders(headers, extraNames = []) {
|
|
72523
73102
|
const extra2 = new Set(extraNames.map((n) => n.toLowerCase()));
|
|
@@ -72617,6 +73196,17 @@ async function runProbe(input) {
|
|
|
72617
73196
|
const saved = parsed.save?.length && !sent.result.error ? takeValues(parsed.save, sent) : void 0;
|
|
72618
73197
|
return { ...sent.result, setup, dataRow: parsed.data ? { row: 1, of: parsed.data.rows.length } : void 0, saved };
|
|
72619
73198
|
}
|
|
73199
|
+
async function probeFull(input) {
|
|
73200
|
+
const parsed = parseLab(input);
|
|
73201
|
+
let vars = {};
|
|
73202
|
+
if (parsed.setup) {
|
|
73203
|
+
const step = await runSetupStep(parsed.setup);
|
|
73204
|
+
if (!step.result.ok) return { status: 0, body: "", error: step.result.error };
|
|
73205
|
+
vars = step.vars;
|
|
73206
|
+
}
|
|
73207
|
+
const sent = await sendOnce(fillLab(parsed, vars));
|
|
73208
|
+
return { status: sent.result.status, body: sent.raw.slice(0, 2e7), error: sent.result.error };
|
|
73209
|
+
}
|
|
72620
73210
|
function takeValues(rules, sent) {
|
|
72621
73211
|
const res = { status: sent.result.status, headers: sent.lowered, body: sent.raw, durationMs: sent.result.durationMs };
|
|
72622
73212
|
return rules.map((rule) => {
|
|
@@ -72714,6 +73304,16 @@ async function sendRaw(lab, extra2 = {}) {
|
|
|
72714
73304
|
if (encoded.contentType && !Object.keys(headers).some((k) => k.toLowerCase() === "content-type")) {
|
|
72715
73305
|
headers["content-type"] = encoded.contentType;
|
|
72716
73306
|
}
|
|
73307
|
+
let target = url2;
|
|
73308
|
+
if (lab.auth.mode === "oauth1") {
|
|
73309
|
+
const { mode: _mode, ...creds } = lab.auth;
|
|
73310
|
+
const contentType = Object.entries(headers).find(([k]) => k.toLowerCase() === "content-type")?.[1];
|
|
73311
|
+
const signed = signOAuth1(creds, { method: lab.request.method, url: url2.toString(), contentType, body: encoded.body });
|
|
73312
|
+
if (signed.authorization) {
|
|
73313
|
+
headers.authorization = signed.authorization;
|
|
73314
|
+
secretNames.push("authorization");
|
|
73315
|
+
} else target = new URL(signed.url);
|
|
73316
|
+
}
|
|
72717
73317
|
if (lab.auth.mode === "aws_sigv4") {
|
|
72718
73318
|
const { mode: _mode, ...creds } = lab.auth;
|
|
72719
73319
|
Object.assign(headers, signAws(creds, { method: lab.request.method, url: url2.toString(), headers, body: encoded.body }));
|
|
@@ -72721,20 +73321,39 @@ async function sendRaw(lab, extra2 = {}) {
|
|
|
72721
73321
|
}
|
|
72722
73322
|
const options = lab.options;
|
|
72723
73323
|
const timeout = durationToMs(options.timeout);
|
|
72724
|
-
const
|
|
72725
|
-
|
|
72726
|
-
|
|
72727
|
-
|
|
73324
|
+
const useJar = options.cookies !== false && !extra2.dispatcher;
|
|
73325
|
+
const ownCookie = Object.keys(headers).some((k) => k.toLowerCase() === "cookie");
|
|
73326
|
+
const agent = extra2.dispatcher ?? new import_undici2.Agent({ connect: await tlsConnect(url2, options.tlsSkipVerify) });
|
|
73327
|
+
const dispatcher = extra2.dispatcher ?? (useJar ? agent : agent.compose(import_undici2.interceptors.redirect({ maxRedirections: options.maxRedirects })));
|
|
72728
73328
|
const started = performance.now();
|
|
72729
73329
|
try {
|
|
72730
|
-
|
|
72731
|
-
|
|
72732
|
-
|
|
72733
|
-
|
|
72734
|
-
|
|
72735
|
-
|
|
72736
|
-
|
|
72737
|
-
|
|
73330
|
+
let current = target;
|
|
73331
|
+
let method = lab.request.method;
|
|
73332
|
+
let body = encoded.body;
|
|
73333
|
+
let response;
|
|
73334
|
+
for (let hops = 0; ; hops++) {
|
|
73335
|
+
const sendHeaders = { ...headers };
|
|
73336
|
+
if (useJar && !ownCookie) {
|
|
73337
|
+
const jar2 = await cookieHeader(current);
|
|
73338
|
+
if (jar2) sendHeaders.cookie = jar2;
|
|
73339
|
+
}
|
|
73340
|
+
response = await (0, import_undici2.request)(current, { method, headers: sendHeaders, body, headersTimeout: timeout, bodyTimeout: timeout, dispatcher });
|
|
73341
|
+
if (!useJar) break;
|
|
73342
|
+
await storeCookies(current, response.headers["set-cookie"]);
|
|
73343
|
+
const location = response.headers.location;
|
|
73344
|
+
if (![301, 302, 303, 307, 308].includes(response.statusCode) || typeof location !== "string" || hops >= options.maxRedirects) break;
|
|
73345
|
+
await response.body.dump();
|
|
73346
|
+
const next = new URL(location, current);
|
|
73347
|
+
if (next.origin !== current.origin) {
|
|
73348
|
+
for (const k of Object.keys(headers)) if (k.toLowerCase() === "authorization") delete headers[k];
|
|
73349
|
+
}
|
|
73350
|
+
if (response.statusCode === 303 || (response.statusCode === 301 || response.statusCode === 302) && method === "POST") {
|
|
73351
|
+
method = "GET";
|
|
73352
|
+
body = void 0;
|
|
73353
|
+
for (const k of Object.keys(headers)) if (k.toLowerCase() === "content-type") delete headers[k];
|
|
73354
|
+
}
|
|
73355
|
+
current = next;
|
|
73356
|
+
}
|
|
72738
73357
|
const raw = await response.body.text();
|
|
72739
73358
|
const durationMs = Math.round((performance.now() - started) * 10) / 10;
|
|
72740
73359
|
const truncated = raw.length > BODY_LIMIT;
|
|
@@ -72789,8 +73408,10 @@ var init_probe = __esm({
|
|
|
72789
73408
|
import_undici2 = __toESM(require_undici(), 1);
|
|
72790
73409
|
init_challenge_auth();
|
|
72791
73410
|
init_aws_sign();
|
|
73411
|
+
init_oauth1_sign();
|
|
72792
73412
|
init_jwt();
|
|
72793
73413
|
init_certificates();
|
|
73414
|
+
init_cookies();
|
|
72794
73415
|
init_oauth();
|
|
72795
73416
|
BODY_LIMIT = 64 * 1024;
|
|
72796
73417
|
}
|
|
@@ -73138,12 +73759,12 @@ function dataDefinition(lab) {
|
|
|
73138
73759
|
};
|
|
73139
73760
|
}
|
|
73140
73761
|
function loadDefinition(run2) {
|
|
73141
|
-
const
|
|
73142
|
-
const rate = isArrivalRate(
|
|
73762
|
+
const load3 = run2.lab.load;
|
|
73763
|
+
const rate = isArrivalRate(load3.executor);
|
|
73143
73764
|
return {
|
|
73144
|
-
executor:
|
|
73145
|
-
rate: rate ?
|
|
73146
|
-
stages:
|
|
73765
|
+
executor: load3.executor,
|
|
73766
|
+
rate: rate ? load3.rate ?? Math.max(0, ...load3.stages.map((s) => s.target)) : void 0,
|
|
73767
|
+
stages: load3.stages.length > 0 ? load3.stages : [{ duration: load3.duration, target: rate ? load3.rate ?? 0 : load3.vus }]
|
|
73147
73768
|
};
|
|
73148
73769
|
}
|
|
73149
73770
|
function liveSummaryParts(run2) {
|
|
@@ -73362,7 +73983,7 @@ async function startRun(lab, repoRoot2) {
|
|
|
73362
73983
|
} catch (err) {
|
|
73363
73984
|
throw Object.assign(new Error(`OAuth 2.0: ${err instanceof Error ? err.message : String(err)}`), { statusCode: 422 });
|
|
73364
73985
|
}
|
|
73365
|
-
refresh = refreshPlan(config2, token);
|
|
73986
|
+
refresh = await refreshPlan(config2, token, loadDurationHintMs(filled.load));
|
|
73366
73987
|
filled = { ...filled, auth: { mode: "bearer", token: token.accessToken } };
|
|
73367
73988
|
}
|
|
73368
73989
|
let tlsAuth;
|
|
@@ -73382,7 +74003,19 @@ async function startRun(lab, repoRoot2) {
|
|
|
73382
74003
|
}
|
|
73383
74004
|
filled = { ...filled, auth: { mode: "none" } };
|
|
73384
74005
|
}
|
|
74006
|
+
const bundle = await caBundle();
|
|
74007
|
+
if (bundle && process.platform !== "linux" && !filled.options.tlsSkipVerify && await needsTrustedCa(filled.request.url)) {
|
|
74008
|
+
filled = { ...filled, options: { ...filled.options, tlsSkipVerify: true } };
|
|
74009
|
+
}
|
|
73385
74010
|
const generated = generateK6Script(filled, { oauthRefresh: refresh, tlsAuth, jwt: jwt2 });
|
|
74011
|
+
if (filled.options.cookies !== false && !filled.request.headers.some((h) => h.key.toLowerCase() === "cookie")) {
|
|
74012
|
+
const jar2 = await cookieHeader(filled.request.url);
|
|
74013
|
+
if (jar2) generated.env.ATC_COOKIE = jar2;
|
|
74014
|
+
}
|
|
74015
|
+
if (bundle && process.platform === "linux") {
|
|
74016
|
+
await writeFile3(join5(dir2, "ca-bundle.pem"), bundle, "utf8");
|
|
74017
|
+
generated.env.SSL_CERT_FILE = join5(dir2, "ca-bundle.pem");
|
|
74018
|
+
}
|
|
73386
74019
|
await writeFile3(scriptPath, generated.script, "utf8");
|
|
73387
74020
|
for (const [name, text] of Object.entries(generated.files)) await writeFile3(join5(dir2, name), text, "utf8");
|
|
73388
74021
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -73529,6 +74162,7 @@ var init_load_run = __esm({
|
|
|
73529
74162
|
init_k6_script();
|
|
73530
74163
|
init_src();
|
|
73531
74164
|
init_certificates();
|
|
74165
|
+
init_cookies();
|
|
73532
74166
|
init_jwt();
|
|
73533
74167
|
init_oauth();
|
|
73534
74168
|
init_probe();
|
|
@@ -73542,7 +74176,7 @@ var init_load_run = __esm({
|
|
|
73542
74176
|
});
|
|
73543
74177
|
|
|
73544
74178
|
// ../../apps/engine/src/secret-store.ts
|
|
73545
|
-
import { createCipheriv, createDecipheriv, randomBytes as
|
|
74179
|
+
import { createCipheriv, createDecipheriv, randomBytes as randomBytes4 } from "node:crypto";
|
|
73546
74180
|
import { readFile as readFile4, rename as rename3, writeFile as writeFile4 } from "node:fs/promises";
|
|
73547
74181
|
import { join as join6 } from "node:path";
|
|
73548
74182
|
var SecretStore;
|
|
@@ -73579,7 +74213,7 @@ var init_secret_store = __esm({
|
|
|
73579
74213
|
}
|
|
73580
74214
|
async persist() {
|
|
73581
74215
|
if (!this.key) return;
|
|
73582
|
-
const iv =
|
|
74216
|
+
const iv = randomBytes4(12);
|
|
73583
74217
|
const cipher = createCipheriv("aes-256-gcm", this.key, iv);
|
|
73584
74218
|
const body = Buffer.concat([cipher.update(JSON.stringify(Object.fromEntries(this.values)), "utf8"), cipher.final()]);
|
|
73585
74219
|
const tmp = `${this.file}.${process.pid}.tmp`;
|
|
@@ -78624,14 +79258,14 @@ var require_wrap_thenable = __commonJS({
|
|
|
78624
79258
|
var { setErrorStatusCode } = require_error_status();
|
|
78625
79259
|
var diagnostics = __require("node:diagnostics_channel");
|
|
78626
79260
|
var channels = diagnostics.tracingChannel("fastify.request.handler");
|
|
78627
|
-
function wrapThenable(thenable, reply,
|
|
78628
|
-
if (
|
|
79261
|
+
function wrapThenable(thenable, reply, store3) {
|
|
79262
|
+
if (store3) store3.async = true;
|
|
78629
79263
|
thenable.then(function(payload) {
|
|
78630
79264
|
if (reply[kReplyHijacked] === true) {
|
|
78631
79265
|
return;
|
|
78632
79266
|
}
|
|
78633
|
-
if (
|
|
78634
|
-
channels.asyncStart.publish(
|
|
79267
|
+
if (store3) {
|
|
79268
|
+
channels.asyncStart.publish(store3);
|
|
78635
79269
|
}
|
|
78636
79270
|
try {
|
|
78637
79271
|
if (payload !== void 0 || //
|
|
@@ -78645,16 +79279,16 @@ var require_wrap_thenable = __commonJS({
|
|
|
78645
79279
|
}
|
|
78646
79280
|
}
|
|
78647
79281
|
} finally {
|
|
78648
|
-
if (
|
|
78649
|
-
channels.asyncEnd.publish(
|
|
79282
|
+
if (store3) {
|
|
79283
|
+
channels.asyncEnd.publish(store3);
|
|
78650
79284
|
}
|
|
78651
79285
|
}
|
|
78652
79286
|
}, function(err) {
|
|
78653
|
-
if (
|
|
78654
|
-
|
|
79287
|
+
if (store3) {
|
|
79288
|
+
store3.error = err;
|
|
78655
79289
|
setErrorStatusCode(reply, err);
|
|
78656
|
-
channels.error.publish(
|
|
78657
|
-
channels.asyncStart.publish(
|
|
79290
|
+
channels.error.publish(store3);
|
|
79291
|
+
channels.asyncStart.publish(store3);
|
|
78658
79292
|
}
|
|
78659
79293
|
try {
|
|
78660
79294
|
if (reply.sent === true) {
|
|
@@ -78666,8 +79300,8 @@ var require_wrap_thenable = __commonJS({
|
|
|
78666
79300
|
} catch (err2) {
|
|
78667
79301
|
reply.send(err2);
|
|
78668
79302
|
} finally {
|
|
78669
|
-
if (
|
|
78670
|
-
channels.asyncEnd.publish(
|
|
79303
|
+
if (store3) {
|
|
79304
|
+
channels.asyncEnd.publish(store3);
|
|
78671
79305
|
}
|
|
78672
79306
|
}
|
|
78673
79307
|
});
|
|
@@ -78994,10 +79628,10 @@ var require_content_type = __commonJS({
|
|
|
78994
79628
|
this.#subtype = subtype;
|
|
78995
79629
|
this.#valid = true;
|
|
78996
79630
|
this.#empty = false;
|
|
78997
|
-
let
|
|
78998
|
-
while (
|
|
78999
|
-
const key =
|
|
79000
|
-
let value =
|
|
79631
|
+
let matches2 = keyValuePairsReg.exec(paramsList);
|
|
79632
|
+
while (matches2) {
|
|
79633
|
+
const key = matches2[1].toLowerCase();
|
|
79634
|
+
let value = matches2[2];
|
|
79001
79635
|
if (value.charCodeAt(0) === 34) {
|
|
79002
79636
|
value = value.slice(1, -1);
|
|
79003
79637
|
if (value.indexOf("\\") !== -1) {
|
|
@@ -79005,7 +79639,7 @@ var require_content_type = __commonJS({
|
|
|
79005
79639
|
}
|
|
79006
79640
|
}
|
|
79007
79641
|
this.#parameters.set(key, value);
|
|
79008
|
-
|
|
79642
|
+
matches2 = keyValuePairsReg.exec(paramsList);
|
|
79009
79643
|
}
|
|
79010
79644
|
}
|
|
79011
79645
|
get [Symbol.toStringTag]() {
|
|
@@ -79183,7 +79817,7 @@ var require_handle_request = __commonJS({
|
|
|
79183
79817
|
if (!channels.hasSubscribers || context[kFourOhFourContext] === null) {
|
|
79184
79818
|
preHandlerCallbackInner(err, request3, reply);
|
|
79185
79819
|
} else {
|
|
79186
|
-
const
|
|
79820
|
+
const store3 = {
|
|
79187
79821
|
request: request3,
|
|
79188
79822
|
reply,
|
|
79189
79823
|
async: false,
|
|
@@ -79192,19 +79826,19 @@ var require_handle_request = __commonJS({
|
|
|
79192
79826
|
method: context.config.method
|
|
79193
79827
|
}
|
|
79194
79828
|
};
|
|
79195
|
-
reply[kDiagnosticsStore] =
|
|
79196
|
-
channels.start.runStores(
|
|
79829
|
+
reply[kDiagnosticsStore] = store3;
|
|
79830
|
+
channels.start.runStores(store3, preHandlerCallbackInner, void 0, err, request3, reply, store3);
|
|
79197
79831
|
}
|
|
79198
79832
|
}
|
|
79199
|
-
function preHandlerCallbackInner(err, request3, reply,
|
|
79833
|
+
function preHandlerCallbackInner(err, request3, reply, store3) {
|
|
79200
79834
|
const context = request3[kRouteContext];
|
|
79201
79835
|
try {
|
|
79202
79836
|
if (err != null) {
|
|
79203
79837
|
reply[kReplyIsError] = true;
|
|
79204
|
-
if (
|
|
79205
|
-
|
|
79838
|
+
if (store3) {
|
|
79839
|
+
store3.error = err;
|
|
79206
79840
|
setErrorStatusCode(reply, err);
|
|
79207
|
-
channels.error.publish(
|
|
79841
|
+
channels.error.publish(store3);
|
|
79208
79842
|
}
|
|
79209
79843
|
reply.send(err);
|
|
79210
79844
|
return;
|
|
@@ -79213,10 +79847,10 @@ var require_handle_request = __commonJS({
|
|
|
79213
79847
|
try {
|
|
79214
79848
|
result = context.handler(request3, reply);
|
|
79215
79849
|
} catch (err2) {
|
|
79216
|
-
if (
|
|
79217
|
-
|
|
79850
|
+
if (store3) {
|
|
79851
|
+
store3.error = err2;
|
|
79218
79852
|
setErrorStatusCode(reply, err2);
|
|
79219
|
-
channels.error.publish(
|
|
79853
|
+
channels.error.publish(store3);
|
|
79220
79854
|
}
|
|
79221
79855
|
reply[kReplyIsError] = true;
|
|
79222
79856
|
reply.send(err2);
|
|
@@ -79224,13 +79858,13 @@ var require_handle_request = __commonJS({
|
|
|
79224
79858
|
}
|
|
79225
79859
|
if (result !== void 0) {
|
|
79226
79860
|
if (result !== null && typeof result.then === "function") {
|
|
79227
|
-
wrapThenable(result, reply,
|
|
79861
|
+
wrapThenable(result, reply, store3);
|
|
79228
79862
|
} else {
|
|
79229
79863
|
reply.send(result);
|
|
79230
79864
|
}
|
|
79231
79865
|
}
|
|
79232
79866
|
} finally {
|
|
79233
|
-
if (
|
|
79867
|
+
if (store3) channels.end.publish(store3);
|
|
79234
79868
|
}
|
|
79235
79869
|
}
|
|
79236
79870
|
module.exports = handleRequest;
|
|
@@ -83627,8 +84261,8 @@ var require_tools = __commonJS({
|
|
|
83627
84261
|
if (asJsonChan.hasSubscribers === false) {
|
|
83628
84262
|
return _asJson.call(this, obj, msg, num3, time3);
|
|
83629
84263
|
}
|
|
83630
|
-
const
|
|
83631
|
-
return asJsonChan.traceSync(_asJson,
|
|
84264
|
+
const store3 = { instance: this, arguments };
|
|
84265
|
+
return asJsonChan.traceSync(_asJson, store3, this, obj, msg, num3, time3);
|
|
83632
84266
|
}
|
|
83633
84267
|
function _asJson(obj, msg, num3, time3) {
|
|
83634
84268
|
const stringify2 = this[stringifySym];
|
|
@@ -85896,8 +86530,8 @@ var require_error_handler = __commonJS({
|
|
|
85896
86530
|
const result = func(error51, reply.request, reply);
|
|
85897
86531
|
if (result !== void 0) {
|
|
85898
86532
|
if (result !== null && typeof result.then === "function") {
|
|
85899
|
-
const
|
|
85900
|
-
wrapThenable(result, reply,
|
|
86533
|
+
const store3 = reply[kDiagnosticsStore] || null;
|
|
86534
|
+
wrapThenable(result, reply, store3);
|
|
85901
86535
|
} else {
|
|
85902
86536
|
reply.send(result);
|
|
85903
86537
|
}
|
|
@@ -88216,11 +88850,11 @@ var require_request3 = __commonJS({
|
|
|
88216
88850
|
port: {
|
|
88217
88851
|
get() {
|
|
88218
88852
|
const portReg = /(?<port>:\d+)$/;
|
|
88219
|
-
const
|
|
88220
|
-
if (
|
|
88853
|
+
const matches2 = portReg.exec(this.host);
|
|
88854
|
+
if (matches2 === null || matches2[1] === void 0) {
|
|
88221
88855
|
return null;
|
|
88222
88856
|
}
|
|
88223
|
-
return parseInt(
|
|
88857
|
+
return parseInt(matches2.groups.port.slice(1), 10);
|
|
88224
88858
|
}
|
|
88225
88859
|
},
|
|
88226
88860
|
protocol: {
|
|
@@ -91627,11 +92261,11 @@ var require_validate = __commonJS({
|
|
|
91627
92261
|
jsonPointer = $data;
|
|
91628
92262
|
data = names_1.default.rootData;
|
|
91629
92263
|
} else {
|
|
91630
|
-
const
|
|
91631
|
-
if (!
|
|
92264
|
+
const matches2 = RELATIVE_JSON_POINTER.exec($data);
|
|
92265
|
+
if (!matches2)
|
|
91632
92266
|
throw new Error(`Invalid JSON-pointer: ${$data}`);
|
|
91633
|
-
const up = +
|
|
91634
|
-
jsonPointer =
|
|
92267
|
+
const up = +matches2[1];
|
|
92268
|
+
jsonPointer = matches2[2];
|
|
91635
92269
|
if (jsonPointer === "#") {
|
|
91636
92270
|
if (up >= dataLevel)
|
|
91637
92271
|
throw new Error(errorMsg("property/index", up));
|
|
@@ -92328,11 +92962,11 @@ var require_schemes = __commonJS({
|
|
|
92328
92962
|
urnComponent.error = "URN can not be parsed";
|
|
92329
92963
|
return urnComponent;
|
|
92330
92964
|
}
|
|
92331
|
-
const
|
|
92332
|
-
if (
|
|
92965
|
+
const matches2 = urnComponent.path.match(URN_REG);
|
|
92966
|
+
if (matches2) {
|
|
92333
92967
|
const scheme = options.scheme || urnComponent.scheme || "urn";
|
|
92334
|
-
urnComponent.nid =
|
|
92335
|
-
urnComponent.nss =
|
|
92968
|
+
urnComponent.nid = matches2[1].toLowerCase();
|
|
92969
|
+
urnComponent.nss = matches2[2];
|
|
92336
92970
|
const urnScheme = `${scheme}:${options.nid || urnComponent.nid}`;
|
|
92337
92971
|
const schemeHandler = getSchemeHandler(urnScheme);
|
|
92338
92972
|
urnComponent.path = void 0;
|
|
@@ -92609,8 +93243,8 @@ var require_fast_uri = __commonJS({
|
|
|
92609
93243
|
var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
|
|
92610
93244
|
var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
|
|
92611
93245
|
var AUTHORITY_INTRODUCER_REGION = /^(?:[^#/:?]+:)?([/\\\t\n\r]*)/;
|
|
92612
|
-
function getParseError(parsed,
|
|
92613
|
-
if (
|
|
93246
|
+
function getParseError(parsed, matches2) {
|
|
93247
|
+
if (matches2[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
|
|
92614
93248
|
return 'URI path must start with "/" when authority is present.';
|
|
92615
93249
|
}
|
|
92616
93250
|
if (typeof parsed.port === "number" && (parsed.port < 0 || parsed.port > 65535)) {
|
|
@@ -92657,19 +93291,19 @@ var require_fast_uri = __commonJS({
|
|
|
92657
93291
|
}
|
|
92658
93292
|
}
|
|
92659
93293
|
}
|
|
92660
|
-
const
|
|
92661
|
-
if (
|
|
92662
|
-
parsed.scheme =
|
|
92663
|
-
parsed.userinfo =
|
|
92664
|
-
parsed.host =
|
|
92665
|
-
parsed.port = parseInt(
|
|
92666
|
-
parsed.path =
|
|
92667
|
-
parsed.query =
|
|
92668
|
-
parsed.fragment =
|
|
93294
|
+
const matches2 = uri.match(URI_PARSE);
|
|
93295
|
+
if (matches2) {
|
|
93296
|
+
parsed.scheme = matches2[1];
|
|
93297
|
+
parsed.userinfo = matches2[3];
|
|
93298
|
+
parsed.host = matches2[4];
|
|
93299
|
+
parsed.port = parseInt(matches2[5], 10);
|
|
93300
|
+
parsed.path = matches2[6] || "";
|
|
93301
|
+
parsed.query = matches2[7];
|
|
93302
|
+
parsed.fragment = matches2[8];
|
|
92669
93303
|
if (isNaN(parsed.port)) {
|
|
92670
|
-
parsed.port =
|
|
93304
|
+
parsed.port = matches2[5];
|
|
92671
93305
|
}
|
|
92672
|
-
const parseError = getParseError(parsed,
|
|
93306
|
+
const parseError = getParseError(parsed, matches2);
|
|
92673
93307
|
if (parseError !== void 0) {
|
|
92674
93308
|
parsed.error = parsed.error || parseError;
|
|
92675
93309
|
malformedAuthorityOrPort = true;
|
|
@@ -95547,24 +96181,24 @@ var require_timestamp2 = __commonJS({
|
|
|
95547
96181
|
}
|
|
95548
96182
|
exports.default = validTimestamp;
|
|
95549
96183
|
function validDate(str2) {
|
|
95550
|
-
const
|
|
95551
|
-
if (!
|
|
96184
|
+
const matches2 = DATE.exec(str2);
|
|
96185
|
+
if (!matches2)
|
|
95552
96186
|
return false;
|
|
95553
|
-
const y = +
|
|
95554
|
-
const m = +
|
|
95555
|
-
const d = +
|
|
96187
|
+
const y = +matches2[1];
|
|
96188
|
+
const m = +matches2[2];
|
|
96189
|
+
const d = +matches2[3];
|
|
95556
96190
|
return m >= 1 && m <= 12 && d >= 1 && (d <= DAYS[m] || // leap year: https://tools.ietf.org/html/rfc3339#appendix-C
|
|
95557
96191
|
m === 2 && d === 29 && (y % 100 === 0 ? y % 400 === 0 : y % 4 === 0));
|
|
95558
96192
|
}
|
|
95559
96193
|
function validTime(str2) {
|
|
95560
|
-
const
|
|
95561
|
-
if (!
|
|
96194
|
+
const matches2 = TIME.exec(str2);
|
|
96195
|
+
if (!matches2)
|
|
95562
96196
|
return false;
|
|
95563
|
-
const hr = +
|
|
95564
|
-
const min = +
|
|
95565
|
-
const sec = +
|
|
95566
|
-
const tzH = +(
|
|
95567
|
-
const tzM = +(
|
|
96197
|
+
const hr = +matches2[1];
|
|
96198
|
+
const min = +matches2[2];
|
|
96199
|
+
const sec = +matches2[3];
|
|
96200
|
+
const tzH = +(matches2[4] || 0);
|
|
96201
|
+
const tzM = +(matches2[5] || 0);
|
|
95568
96202
|
return hr <= 23 && min <= 59 && sec <= 59 || // leap second
|
|
95569
96203
|
hr - tzH === 23 && min - tzM === 59 && sec === 60;
|
|
95570
96204
|
}
|
|
@@ -96518,19 +97152,19 @@ var require_parseJson = __commonJS({
|
|
|
96518
97152
|
function parseJson(s, pos) {
|
|
96519
97153
|
let endPos;
|
|
96520
97154
|
parseJson.message = void 0;
|
|
96521
|
-
let
|
|
97155
|
+
let matches2;
|
|
96522
97156
|
if (pos)
|
|
96523
97157
|
s = s.slice(pos);
|
|
96524
97158
|
try {
|
|
96525
97159
|
parseJson.position = pos + s.length;
|
|
96526
97160
|
return JSON.parse(s);
|
|
96527
97161
|
} catch (e) {
|
|
96528
|
-
|
|
96529
|
-
if (!
|
|
97162
|
+
matches2 = rxParseJson.exec(e.message);
|
|
97163
|
+
if (!matches2) {
|
|
96530
97164
|
parseJson.message = "unexpected end";
|
|
96531
97165
|
return void 0;
|
|
96532
97166
|
}
|
|
96533
|
-
endPos = +
|
|
97167
|
+
endPos = +matches2[1];
|
|
96534
97168
|
const c = s[endPos];
|
|
96535
97169
|
s = s.slice(0, endPos);
|
|
96536
97170
|
parseJson.position = pos + endPos;
|
|
@@ -97612,11 +98246,11 @@ var require_schemes2 = __commonJS({
|
|
|
97612
98246
|
urnComponent.error = "URN can not be parsed";
|
|
97613
98247
|
return urnComponent;
|
|
97614
98248
|
}
|
|
97615
|
-
const
|
|
97616
|
-
if (
|
|
98249
|
+
const matches2 = urnComponent.path.match(URN_REG);
|
|
98250
|
+
if (matches2) {
|
|
97617
98251
|
const scheme = options.scheme || urnComponent.scheme || "urn";
|
|
97618
|
-
urnComponent.nid =
|
|
97619
|
-
urnComponent.nss =
|
|
98252
|
+
urnComponent.nid = matches2[1].toLowerCase();
|
|
98253
|
+
urnComponent.nss = matches2[2];
|
|
97620
98254
|
const urnScheme = `${scheme}:${options.nid || urnComponent.nid}`;
|
|
97621
98255
|
const schemeHandler = getSchemeHandler(urnScheme);
|
|
97622
98256
|
urnComponent.path = void 0;
|
|
@@ -97893,8 +98527,8 @@ var require_fast_uri2 = __commonJS({
|
|
|
97893
98527
|
var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
|
|
97894
98528
|
var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
|
|
97895
98529
|
var AUTHORITY_INTRODUCER_REGION = /^(?:[^#/:?]+:)?([/\\\t\n\r]*)/;
|
|
97896
|
-
function getParseError(parsed,
|
|
97897
|
-
if (
|
|
98530
|
+
function getParseError(parsed, matches2) {
|
|
98531
|
+
if (matches2[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
|
|
97898
98532
|
return 'URI path must start with "/" when authority is present.';
|
|
97899
98533
|
}
|
|
97900
98534
|
if (typeof parsed.port === "number" && (parsed.port < 0 || parsed.port > 65535)) {
|
|
@@ -97941,19 +98575,19 @@ var require_fast_uri2 = __commonJS({
|
|
|
97941
98575
|
}
|
|
97942
98576
|
}
|
|
97943
98577
|
}
|
|
97944
|
-
const
|
|
97945
|
-
if (
|
|
97946
|
-
parsed.scheme =
|
|
97947
|
-
parsed.userinfo =
|
|
97948
|
-
parsed.host =
|
|
97949
|
-
parsed.port = parseInt(
|
|
97950
|
-
parsed.path =
|
|
97951
|
-
parsed.query =
|
|
97952
|
-
parsed.fragment =
|
|
98578
|
+
const matches2 = uri.match(URI_PARSE);
|
|
98579
|
+
if (matches2) {
|
|
98580
|
+
parsed.scheme = matches2[1] === void 0 ? void 0 : matches2[1].toLowerCase();
|
|
98581
|
+
parsed.userinfo = matches2[3];
|
|
98582
|
+
parsed.host = matches2[4];
|
|
98583
|
+
parsed.port = parseInt(matches2[5], 10);
|
|
98584
|
+
parsed.path = matches2[6] || "";
|
|
98585
|
+
parsed.query = matches2[7];
|
|
98586
|
+
parsed.fragment = matches2[8];
|
|
97953
98587
|
if (isNaN(parsed.port)) {
|
|
97954
|
-
parsed.port =
|
|
98588
|
+
parsed.port = matches2[5];
|
|
97955
98589
|
}
|
|
97956
|
-
const parseError = getParseError(parsed,
|
|
98590
|
+
const parseError = getParseError(parsed, matches2);
|
|
97957
98591
|
if (parseError !== void 0) {
|
|
97958
98592
|
parsed.error = parsed.error || parseError;
|
|
97959
98593
|
malformedAuthorityOrPort = true;
|
|
@@ -98150,12 +98784,12 @@ var require_formats = __commonJS({
|
|
|
98150
98784
|
var DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
|
|
98151
98785
|
var DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
98152
98786
|
function date5(str2) {
|
|
98153
|
-
const
|
|
98154
|
-
if (!
|
|
98787
|
+
const matches2 = DATE.exec(str2);
|
|
98788
|
+
if (!matches2)
|
|
98155
98789
|
return false;
|
|
98156
|
-
const year = +
|
|
98157
|
-
const month = +
|
|
98158
|
-
const day = +
|
|
98790
|
+
const year = +matches2[1];
|
|
98791
|
+
const month = +matches2[2];
|
|
98792
|
+
const day = +matches2[3];
|
|
98159
98793
|
return month >= 1 && month <= 12 && day >= 1 && day <= (month === 2 && isLeapYear(year) ? 29 : DAYS[month]);
|
|
98160
98794
|
}
|
|
98161
98795
|
function compareDate(d1, d2) {
|
|
@@ -98170,16 +98804,16 @@ var require_formats = __commonJS({
|
|
|
98170
98804
|
var TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
|
|
98171
98805
|
function getTime(strictTimeZone) {
|
|
98172
98806
|
return function time3(str2) {
|
|
98173
|
-
const
|
|
98174
|
-
if (!
|
|
98807
|
+
const matches2 = TIME.exec(str2);
|
|
98808
|
+
if (!matches2)
|
|
98175
98809
|
return false;
|
|
98176
|
-
const hr = +
|
|
98177
|
-
const min = +
|
|
98178
|
-
const sec = +
|
|
98179
|
-
const tz =
|
|
98180
|
-
const tzSign =
|
|
98181
|
-
const tzH = +(
|
|
98182
|
-
const tzM = +(
|
|
98810
|
+
const hr = +matches2[1];
|
|
98811
|
+
const min = +matches2[2];
|
|
98812
|
+
const sec = +matches2[3];
|
|
98813
|
+
const tz = matches2[4];
|
|
98814
|
+
const tzSign = matches2[5] === "-" ? -1 : 1;
|
|
98815
|
+
const tzH = +(matches2[6] || 0);
|
|
98816
|
+
const tzM = +(matches2[7] || 0);
|
|
98183
98817
|
if (tzH > 23 || tzM > 59 || strictTimeZone && !tz)
|
|
98184
98818
|
return false;
|
|
98185
98819
|
if (hr <= 23 && min <= 59 && sec < 60)
|
|
@@ -105663,8 +106297,8 @@ var require_http_method = __commonJS({
|
|
|
105663
106297
|
get: (type) => {
|
|
105664
106298
|
return handlers.get(type) || null;
|
|
105665
106299
|
},
|
|
105666
|
-
set: (type,
|
|
105667
|
-
handlers.set(type,
|
|
106300
|
+
set: (type, store3) => {
|
|
106301
|
+
handlers.set(type, store3);
|
|
105668
106302
|
}
|
|
105669
106303
|
};
|
|
105670
106304
|
},
|
|
@@ -105903,14 +106537,14 @@ var require_handler_storage = __commonJS({
|
|
|
105903
106537
|
// Builds a store object that maps from constraint values to a bitmap of handler indexes which pass the constraint for a value
|
|
105904
106538
|
// So for a host constraint, this might look like { "fastify.io": 0b0010, "google.ca": 0b0101 }, meaning the 3rd handler is constrainted to fastify.io, and the 2nd and 4th handlers are constrained to google.ca.
|
|
105905
106539
|
// The store's implementation comes from the strategies provided to the Router.
|
|
105906
|
-
_buildConstraintStore(
|
|
106540
|
+
_buildConstraintStore(store3, constraint) {
|
|
105907
106541
|
for (let i = 0; i < this.handlers.length; i++) {
|
|
105908
106542
|
const handler = this.handlers[i];
|
|
105909
106543
|
const constraintValue = handler.constraints[constraint];
|
|
105910
106544
|
if (constraintValue !== void 0) {
|
|
105911
|
-
let indexes =
|
|
106545
|
+
let indexes = store3.get(constraintValue) || 0;
|
|
105912
106546
|
indexes |= 1 << i;
|
|
105913
|
-
|
|
106547
|
+
store3.set(constraintValue, indexes);
|
|
105914
106548
|
}
|
|
105915
106549
|
}
|
|
105916
106550
|
}
|
|
@@ -105935,9 +106569,9 @@ var require_handler_storage = __commonJS({
|
|
|
105935
106569
|
_compileGetHandlerMatchingConstraints(constrainer) {
|
|
105936
106570
|
this.constrainedHandlerStores = {};
|
|
105937
106571
|
for (const constraint of this.constraints) {
|
|
105938
|
-
const
|
|
105939
|
-
this.constrainedHandlerStores[constraint] =
|
|
105940
|
-
this._buildConstraintStore(
|
|
106572
|
+
const store3 = constrainer.newStoreForConstraint(constraint);
|
|
106573
|
+
this.constrainedHandlerStores[constraint] = store3;
|
|
106574
|
+
this._buildConstraintStore(store3, constraint);
|
|
105941
106575
|
}
|
|
105942
106576
|
const lines = [];
|
|
105943
106577
|
lines.push(`
|
|
@@ -106200,7 +106834,7 @@ var require_accept_version = __commonJS({
|
|
|
106200
106834
|
this.maxMinors = {};
|
|
106201
106835
|
this.maxPatches = {};
|
|
106202
106836
|
}
|
|
106203
|
-
SemVerStore.prototype.set = function(version2,
|
|
106837
|
+
SemVerStore.prototype.set = function(version2, store3) {
|
|
106204
106838
|
if (typeof version2 !== "string") {
|
|
106205
106839
|
throw new TypeError("Version should be a string");
|
|
106206
106840
|
}
|
|
@@ -106213,21 +106847,21 @@ var require_accept_version = __commonJS({
|
|
|
106213
106847
|
patch = Number(patch) || 0;
|
|
106214
106848
|
if (major >= this.maxMajor) {
|
|
106215
106849
|
this.maxMajor = major;
|
|
106216
|
-
this.store.set("x",
|
|
106217
|
-
this.store.set("*",
|
|
106218
|
-
this.store.set("x.x",
|
|
106219
|
-
this.store.set("x.x.x",
|
|
106850
|
+
this.store.set("x", store3);
|
|
106851
|
+
this.store.set("*", store3);
|
|
106852
|
+
this.store.set("x.x", store3);
|
|
106853
|
+
this.store.set("x.x.x", store3);
|
|
106220
106854
|
}
|
|
106221
106855
|
if (minor >= (this.maxMinors[major] || 0)) {
|
|
106222
106856
|
this.maxMinors[major] = minor;
|
|
106223
|
-
this.store.set(`${major}.x`,
|
|
106224
|
-
this.store.set(`${major}.x.x`,
|
|
106857
|
+
this.store.set(`${major}.x`, store3);
|
|
106858
|
+
this.store.set(`${major}.x.x`, store3);
|
|
106225
106859
|
}
|
|
106226
106860
|
if (patch >= (this.maxPatches[`${major}.${minor}`] || 0)) {
|
|
106227
106861
|
this.maxPatches[`${major}.${minor}`] = patch;
|
|
106228
|
-
this.store.set(`${major}.${minor}.x`,
|
|
106862
|
+
this.store.set(`${major}.${minor}.x`, store3);
|
|
106229
106863
|
}
|
|
106230
|
-
this.store.set(`${major}.${minor}.${patch}`,
|
|
106864
|
+
this.store.set(`${major}.${minor}.${patch}`, store3);
|
|
106231
106865
|
return this;
|
|
106232
106866
|
};
|
|
106233
106867
|
SemVerStore.prototype.get = function(version2) {
|
|
@@ -106695,10 +107329,10 @@ var require_find_my_way = __commonJS({
|
|
|
106695
107329
|
this.trees = /* @__PURE__ */ Object.create(null);
|
|
106696
107330
|
this._treeGET = null;
|
|
106697
107331
|
}
|
|
106698
|
-
Router.prototype.on = function on(method, path, opts, handler,
|
|
107332
|
+
Router.prototype.on = function on(method, path, opts, handler, store3) {
|
|
106699
107333
|
if (typeof opts === "function") {
|
|
106700
107334
|
if (handler !== void 0) {
|
|
106701
|
-
|
|
107335
|
+
store3 = handler;
|
|
106702
107336
|
}
|
|
106703
107337
|
handler = opts;
|
|
106704
107338
|
opts = {};
|
|
@@ -106712,8 +107346,8 @@ var require_find_my_way = __commonJS({
|
|
|
106712
107346
|
assert2(path.length === optionalParamMatch.index + optionalParamMatch[0].length, "Optional Parameter needs to be the last parameter of the path");
|
|
106713
107347
|
const pathFull = path.replace(OPTIONAL_PARAM_REGEXP, "$1$2");
|
|
106714
107348
|
const pathOptional = path.replace(OPTIONAL_PARAM_REGEXP, "$2") || "/";
|
|
106715
|
-
this.on(method, pathFull, opts, handler,
|
|
106716
|
-
this.on(method, pathOptional, opts, handler,
|
|
107349
|
+
this.on(method, pathFull, opts, handler, store3);
|
|
107350
|
+
this.on(method, pathOptional, opts, handler, store3);
|
|
106717
107351
|
return;
|
|
106718
107352
|
}
|
|
106719
107353
|
const route = path;
|
|
@@ -106727,10 +107361,10 @@ var require_find_my_way = __commonJS({
|
|
|
106727
107361
|
for (const method2 of methods) {
|
|
106728
107362
|
assert2(typeof method2 === "string", "Method should be a string");
|
|
106729
107363
|
assert2(httpMethods.includes(method2), `Method '${method2}' is not an http method.`);
|
|
106730
|
-
this._on(method2, path, opts, handler,
|
|
107364
|
+
this._on(method2, path, opts, handler, store3, route);
|
|
106731
107365
|
}
|
|
106732
107366
|
};
|
|
106733
|
-
Router.prototype._on = function _on(method, path, opts, handler,
|
|
107367
|
+
Router.prototype._on = function _on(method, path, opts, handler, store3) {
|
|
106734
107368
|
let constraints = {};
|
|
106735
107369
|
if (opts.constraints !== void 0) {
|
|
106736
107370
|
assert2(typeof opts.constraints === "object" && opts.constraints !== null, "Constraints should be an object");
|
|
@@ -106851,7 +107485,7 @@ var require_find_my_way = __commonJS({
|
|
|
106851
107485
|
throw new Error(`Method '${method}' already declared for route '${pattern}' with constraints '${JSON.stringify(constraints)}'`);
|
|
106852
107486
|
}
|
|
106853
107487
|
}
|
|
106854
|
-
const route = { method, path, pattern, params, opts, handler, store:
|
|
107488
|
+
const route = { method, path, pattern, params, opts, handler, store: store3 };
|
|
106855
107489
|
this.routes.push(route);
|
|
106856
107490
|
currentNode.addRoute(route, this.constrainer);
|
|
106857
107491
|
};
|
|
@@ -107199,8 +107833,8 @@ var require_find_my_way = __commonJS({
|
|
|
107199
107833
|
Router.prototype._rebuild = function(routes2) {
|
|
107200
107834
|
this.reset();
|
|
107201
107835
|
for (const route of routes2) {
|
|
107202
|
-
const { method, path, opts, handler, store:
|
|
107203
|
-
this._on(method, path, opts, handler,
|
|
107836
|
+
const { method, path, opts, handler, store: store3 } = route;
|
|
107837
|
+
this._on(method, path, opts, handler, store3);
|
|
107204
107838
|
}
|
|
107205
107839
|
};
|
|
107206
107840
|
Router.prototype._defaultRoute = function(req, res, ctx) {
|
|
@@ -107260,12 +107894,12 @@ var require_find_my_way = __commonJS({
|
|
|
107260
107894
|
if (!httpMethods.hasOwnProperty(i)) continue;
|
|
107261
107895
|
const m = httpMethods[i];
|
|
107262
107896
|
const methodName = m.toLowerCase();
|
|
107263
|
-
Router.prototype[methodName] = function(path, handler,
|
|
107264
|
-
return this.on(m, path, handler,
|
|
107897
|
+
Router.prototype[methodName] = function(path, handler, store3) {
|
|
107898
|
+
return this.on(m, path, handler, store3);
|
|
107265
107899
|
};
|
|
107266
107900
|
}
|
|
107267
|
-
Router.prototype.all = function(path, handler,
|
|
107268
|
-
this.on(httpMethods, path, handler,
|
|
107901
|
+
Router.prototype.all = function(path, handler, store3) {
|
|
107902
|
+
this.on(httpMethods, path, handler, store3);
|
|
107269
107903
|
};
|
|
107270
107904
|
Router.sanitizeUrlPath = function sanitizeUrlPath(url2, useSemicolonDelimiter) {
|
|
107271
107905
|
const decoded = safeDecodeURI(url2, useSemicolonDelimiter);
|
|
@@ -108145,7 +108779,7 @@ var require_dist5 = __commonJS({
|
|
|
108145
108779
|
exports.stringifyCookie = stringifyCookie;
|
|
108146
108780
|
exports.stringifySetCookie = stringifySetCookie;
|
|
108147
108781
|
exports.serialize = stringifySetCookie;
|
|
108148
|
-
exports.parseSetCookie =
|
|
108782
|
+
exports.parseSetCookie = parseSetCookie2;
|
|
108149
108783
|
exports.stringifySetCookie = stringifySetCookie;
|
|
108150
108784
|
exports.serialize = stringifySetCookie;
|
|
108151
108785
|
var cookieNameRegExp = /^[\u0021-\u003A\u003C\u003E-\u007E]+$/;
|
|
@@ -108282,7 +108916,7 @@ var require_dist5 = __commonJS({
|
|
|
108282
108916
|
}
|
|
108283
108917
|
return str2;
|
|
108284
108918
|
}
|
|
108285
|
-
function
|
|
108919
|
+
function parseSetCookie2(str2, options) {
|
|
108286
108920
|
const dec = options?.decode || decode4;
|
|
108287
108921
|
const len = str2.length;
|
|
108288
108922
|
const endIdx = endIndex(str2, 0, len);
|
|
@@ -110973,43 +111607,43 @@ var init_serve_ui = __esm({
|
|
|
110973
111607
|
});
|
|
110974
111608
|
|
|
110975
111609
|
// ../../apps/engine/src/history-routes.ts
|
|
110976
|
-
function registerHistoryRoutes(app2,
|
|
110977
|
-
app2.get("/history", async () =>
|
|
111610
|
+
function registerHistoryRoutes(app2, store3) {
|
|
111611
|
+
app2.get("/history", async () => store3.index());
|
|
110978
111612
|
app2.get("/history/runs/:id", async (request3, reply) => {
|
|
110979
|
-
const doc = await
|
|
111613
|
+
const doc = await store3.readRun(request3.params.id);
|
|
110980
111614
|
return doc ?? reply.code(404).send({ error: "run not found in history" });
|
|
110981
111615
|
});
|
|
110982
111616
|
app2.get("/history/compares/:id", async (request3, reply) => {
|
|
110983
|
-
const doc = await
|
|
111617
|
+
const doc = await store3.readCompare(request3.params.id);
|
|
110984
111618
|
return doc ?? reply.code(404).send({ error: "compare not found in history" });
|
|
110985
111619
|
});
|
|
110986
111620
|
app2.patch("/history/runs/:id", async (request3, reply) => {
|
|
110987
111621
|
const parsed = runPatch.safeParse(request3.body);
|
|
110988
111622
|
if (!parsed.success) return reply.code(400).send({ error: "invalid update", issues: parsed.error.issues });
|
|
110989
|
-
const summary = await
|
|
111623
|
+
const summary = await store3.updateRun(request3.params.id, parsed.data);
|
|
110990
111624
|
return summary ?? reply.code(404).send({ error: "run not found in history" });
|
|
110991
111625
|
});
|
|
110992
111626
|
app2.patch("/history/compares/:id", async (request3, reply) => {
|
|
110993
111627
|
const parsed = comparePatch.safeParse(request3.body);
|
|
110994
111628
|
if (!parsed.success) return reply.code(400).send({ error: "invalid update", issues: parsed.error.issues });
|
|
110995
|
-
const summary = await
|
|
111629
|
+
const summary = await store3.updateCompare(request3.params.id, parsed.data);
|
|
110996
111630
|
return summary ?? reply.code(404).send({ error: "compare not found in history" });
|
|
110997
111631
|
});
|
|
110998
111632
|
app2.post("/history/delete", async (request3, reply) => {
|
|
110999
111633
|
const parsed = removeBody.safeParse(request3.body);
|
|
111000
111634
|
if (!parsed.success) return reply.code(400).send({ error: "invalid delete", issues: parsed.error.issues });
|
|
111001
|
-
return { removed: await
|
|
111635
|
+
return { removed: await store3.remove(parsed.data) };
|
|
111002
111636
|
});
|
|
111003
111637
|
app2.post("/history/import", async (request3, reply) => {
|
|
111004
111638
|
try {
|
|
111005
111639
|
const raw = request3.body;
|
|
111006
111640
|
const parsed = parseExportFile(raw);
|
|
111007
111641
|
if (parsed.kind === "report") {
|
|
111008
|
-
return { kind: "run", summary: await
|
|
111642
|
+
return { kind: "run", summary: await store3.saveRun(parsed.report, { ...raw?.meta, source: "import" }) };
|
|
111009
111643
|
}
|
|
111010
111644
|
return {
|
|
111011
111645
|
kind: "compare",
|
|
111012
|
-
summary: await
|
|
111646
|
+
summary: await store3.saveCompare({ apis: parsed.doc.apis, mode: parsed.doc.mode, name: raw?.meta?.name, source: "import" })
|
|
111013
111647
|
};
|
|
111014
111648
|
} catch (err) {
|
|
111015
111649
|
return reply.code(400).send({ error: err instanceof Error ? err.message : "Not an APITestCenter report" });
|
|
@@ -111020,7 +111654,7 @@ function registerHistoryRoutes(app2, store2) {
|
|
|
111020
111654
|
if (!parsed.success) return reply.code(400).send({ error: "invalid compare", issues: parsed.error.issues });
|
|
111021
111655
|
const doc = { kind: "compare", apis: parsed.data.apis };
|
|
111022
111656
|
if (!isCompareExport(doc)) return reply.code(400).send({ error: "each API needs a run report" });
|
|
111023
|
-
return
|
|
111657
|
+
return store3.saveCompare({
|
|
111024
111658
|
apis: parsed.data.apis,
|
|
111025
111659
|
mode: parsed.data.mode,
|
|
111026
111660
|
name: parsed.data.name,
|
|
@@ -111031,7 +111665,7 @@ function registerHistoryRoutes(app2, store2) {
|
|
|
111031
111665
|
const parsed = revealBody.safeParse(request3.body ?? {});
|
|
111032
111666
|
if (!parsed.success) return reply.code(400).send({ error: "invalid target" });
|
|
111033
111667
|
const { folder, id } = parsed.data;
|
|
111034
|
-
return { path: await
|
|
111668
|
+
return { path: await store3.reveal(folder && id ? { folder, id } : void 0) };
|
|
111035
111669
|
});
|
|
111036
111670
|
}
|
|
111037
111671
|
var runPatch, comparePatch, removeBody, compareBody, revealBody;
|
|
@@ -111089,53 +111723,53 @@ var init_settings_routes = __esm({
|
|
|
111089
111723
|
});
|
|
111090
111724
|
|
|
111091
111725
|
// ../../apps/engine/src/workspace-routes.ts
|
|
111092
|
-
function registerWorkspaceRoutes(app2,
|
|
111093
|
-
app2.get("/workspace", async () =>
|
|
111094
|
-
app2.get("/workspace/folders", async (request3) =>
|
|
111095
|
-
app2.get("/workspace/folders/chain", async (request3) =>
|
|
111726
|
+
function registerWorkspaceRoutes(app2, store3, onFolderMoved) {
|
|
111727
|
+
app2.get("/workspace", async () => store3.index());
|
|
111728
|
+
app2.get("/workspace/folders", async (request3) => store3.getFolder(request3.query.path ?? ""));
|
|
111729
|
+
app2.get("/workspace/folders/chain", async (request3) => store3.folderChain(request3.query.collection ?? ""));
|
|
111096
111730
|
app2.put("/workspace/folders", async (request3, reply) => {
|
|
111097
111731
|
const parsed = external_exports.object({ path: external_exports.string().min(1).max(400), settings: external_exports.unknown() }).strict().safeParse(request3.body);
|
|
111098
111732
|
if (!parsed.success) return reply.code(400).send({ error: "invalid folder", issues: parsed.error.issues });
|
|
111099
|
-
return
|
|
111733
|
+
return store3.saveFolder(parsed.data.path, parsed.data.settings);
|
|
111100
111734
|
});
|
|
111101
111735
|
app2.post("/workspace/folders/move", async (request3, reply) => {
|
|
111102
111736
|
const parsed = external_exports.object({ from: external_exports.string().min(1).max(400), to: external_exports.string().min(1).max(400) }).strict().safeParse(request3.body);
|
|
111103
111737
|
if (!parsed.success) return reply.code(400).send({ error: "invalid move", issues: parsed.error.issues });
|
|
111104
|
-
const moved = await
|
|
111738
|
+
const moved = await store3.moveFolder(parsed.data.from, parsed.data.to);
|
|
111105
111739
|
await onFolderMoved?.(moved.from, moved.to);
|
|
111106
111740
|
return moved;
|
|
111107
111741
|
});
|
|
111108
111742
|
app2.delete("/workspace/folders", async (request3, reply) => {
|
|
111109
|
-
return await
|
|
111743
|
+
return await store3.deleteFolder(request3.query.path ?? "") ? { removed: 1 } : reply.code(404).send({ error: "folder not found" });
|
|
111110
111744
|
});
|
|
111111
111745
|
app2.get("/workspace/requests/:id", async (request3, reply) => {
|
|
111112
|
-
const saved = await
|
|
111746
|
+
const saved = await store3.getRequest(request3.params.id);
|
|
111113
111747
|
return saved ?? reply.code(404).send({ error: "saved request not found" });
|
|
111114
111748
|
});
|
|
111115
111749
|
app2.post("/workspace/requests", async (request3, reply) => {
|
|
111116
111750
|
const parsed = requestBody.safeParse(request3.body);
|
|
111117
111751
|
if (!parsed.success) return reply.code(400).send({ error: "invalid request", issues: parsed.error.issues });
|
|
111118
|
-
return
|
|
111752
|
+
return store3.saveRequest(parsed.data);
|
|
111119
111753
|
});
|
|
111120
111754
|
app2.put("/workspace/requests/:id", async (request3, reply) => {
|
|
111121
111755
|
const parsed = requestBody.safeParse(request3.body);
|
|
111122
111756
|
if (!parsed.success) return reply.code(400).send({ error: "invalid request", issues: parsed.error.issues });
|
|
111123
|
-
return
|
|
111757
|
+
return store3.saveRequest({ ...parsed.data, id: request3.params.id });
|
|
111124
111758
|
});
|
|
111125
111759
|
app2.delete("/workspace/requests/:id", async (request3, reply) => {
|
|
111126
|
-
return await
|
|
111760
|
+
return await store3.deleteRequest(request3.params.id) ? { removed: 1 } : reply.code(404).send({ error: "saved request not found" });
|
|
111127
111761
|
});
|
|
111128
|
-
app2.put("/workspace/environments", async (request3) =>
|
|
111762
|
+
app2.put("/workspace/environments", async (request3) => store3.saveEnvironments(request3.body));
|
|
111129
111763
|
app2.post("/workspace/import", async (request3, reply) => {
|
|
111130
111764
|
const parsed = importBody.safeParse(request3.body);
|
|
111131
111765
|
if (!parsed.success) return reply.code(400).send({ error: "invalid import", issues: parsed.error.issues });
|
|
111132
111766
|
const saved = [];
|
|
111133
|
-
for (const f of parsed.data.folders) await
|
|
111134
|
-
for (const r of parsed.data.requests) saved.push(await
|
|
111767
|
+
for (const f of parsed.data.folders) await store3.saveFolder(f.path, f.settings);
|
|
111768
|
+
for (const r of parsed.data.requests) saved.push(await store3.saveRequest(r));
|
|
111135
111769
|
let added = 0;
|
|
111136
111770
|
let merged = 0;
|
|
111137
111771
|
if (parsed.data.environments.length) {
|
|
111138
|
-
const envs = await
|
|
111772
|
+
const envs = await store3.environments();
|
|
111139
111773
|
for (const incoming of parsed.data.environments) {
|
|
111140
111774
|
const existing = envs.find((e) => e.name.toLowerCase() === incoming.name.toLowerCase());
|
|
111141
111775
|
if (existing) {
|
|
@@ -111147,7 +111781,7 @@ function registerWorkspaceRoutes(app2, store2, onFolderMoved) {
|
|
|
111147
111781
|
added++;
|
|
111148
111782
|
}
|
|
111149
111783
|
}
|
|
111150
|
-
await
|
|
111784
|
+
await store3.saveEnvironments(envs);
|
|
111151
111785
|
}
|
|
111152
111786
|
return { requests: saved.length, firstId: saved[0]?.id ?? null, environments: { added, merged } };
|
|
111153
111787
|
});
|
|
@@ -111178,7 +111812,7 @@ var init_workspace_routes = __esm({
|
|
|
111178
111812
|
});
|
|
111179
111813
|
|
|
111180
111814
|
// ../../apps/engine/src/demo-api.ts
|
|
111181
|
-
import { randomBytes as
|
|
111815
|
+
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
111182
111816
|
function registerDemoApi(api) {
|
|
111183
111817
|
const tokens = /* @__PURE__ */ new Map();
|
|
111184
111818
|
const created = /* @__PURE__ */ new Map();
|
|
@@ -111204,7 +111838,7 @@ function registerDemoApi(api) {
|
|
|
111204
111838
|
return reply.code(401).send({ error: 'Use any username with the password "demo".' });
|
|
111205
111839
|
}
|
|
111206
111840
|
if (tokens.size > 5e3) tokens.clear();
|
|
111207
|
-
const token = `demo-${
|
|
111841
|
+
const token = `demo-${randomBytes5(12).toString("hex")}`;
|
|
111208
111842
|
tokens.set(token, username);
|
|
111209
111843
|
return { data: { token, user: username, expiresIn: 3600 } };
|
|
111210
111844
|
});
|
|
@@ -111534,6 +112168,8 @@ async function routes(api) {
|
|
|
111534
112168
|
status: "ok",
|
|
111535
112169
|
service: "engine",
|
|
111536
112170
|
version: appVersion,
|
|
112171
|
+
// "cli": started by `atc ui` — the screens say so when it stops.
|
|
112172
|
+
host: process.env.ATC_HOST ?? (uiDir ? "desktop" : "dev"),
|
|
111537
112173
|
persistence: "files",
|
|
111538
112174
|
dataDir: history.dir,
|
|
111539
112175
|
k6: await detectK6(root),
|
|
@@ -111560,6 +112196,13 @@ async function routes(api) {
|
|
|
111560
112196
|
return usage.setEnabled(request3.body.enabled);
|
|
111561
112197
|
});
|
|
111562
112198
|
await api.register(async (demo) => registerDemoApi(demo), { prefix: "/demo-api", logLevel: "warn" });
|
|
112199
|
+
api.post("/graphql/introspect", async (request3, reply) => {
|
|
112200
|
+
try {
|
|
112201
|
+
return await probeFull(request3.body);
|
|
112202
|
+
} catch (err) {
|
|
112203
|
+
return reply.code(400).send({ error: err instanceof Error ? err.message : "invalid request" });
|
|
112204
|
+
}
|
|
112205
|
+
});
|
|
111563
112206
|
api.post("/probe", async (request3, reply) => {
|
|
111564
112207
|
const parsed = labRequestSchema.safeParse(request3.body);
|
|
111565
112208
|
if (!parsed.success) {
|
|
@@ -111598,6 +112241,8 @@ async function routes(api) {
|
|
|
111598
112241
|
useOAuthStore(secrets2);
|
|
111599
112242
|
registerOAuthRoutes(api);
|
|
111600
112243
|
useCertificateStore(history.dir, secrets2);
|
|
112244
|
+
useCookieStore(secrets2);
|
|
112245
|
+
registerCookieRoutes(api);
|
|
111601
112246
|
registerCertificateRoutes(api);
|
|
111602
112247
|
const suitePlans = new SuitePlans(history.dir);
|
|
111603
112248
|
registerWorkspaceRoutes(api, workspace, (from, to) => suitePlans.move(from, to));
|
|
@@ -111639,7 +112284,7 @@ async function routes(api) {
|
|
|
111639
112284
|
function engineUrl() {
|
|
111640
112285
|
return listeningUrl;
|
|
111641
112286
|
}
|
|
111642
|
-
var import_cors, import_dotenv, import_fastify, root, host, port, history, appVersion, usage, app, listeningUrl, uiDir;
|
|
112287
|
+
var import_cors, import_dotenv, import_fastify, root, tlsCheckWasOff, host, port, history, appVersion, usage, app, listeningUrl, uiDir;
|
|
111643
112288
|
var init_index = __esm({
|
|
111644
112289
|
async "../../apps/engine/src/index.ts"() {
|
|
111645
112290
|
"use strict";
|
|
@@ -111662,9 +112307,12 @@ var init_index = __esm({
|
|
|
111662
112307
|
init_suite_run();
|
|
111663
112308
|
init_oauth();
|
|
111664
112309
|
init_certificates();
|
|
112310
|
+
init_cookies();
|
|
111665
112311
|
init_local_servers();
|
|
111666
112312
|
root = resolve3(dirname2(fileURLToPath(import.meta.url)), "../../..");
|
|
111667
112313
|
if (!process.env.ATC_NO_DOTENV) (0, import_dotenv.config)({ path: resolve3(root, ".env") });
|
|
112314
|
+
tlsCheckWasOff = process.env.NODE_TLS_REJECT_UNAUTHORIZED === "0";
|
|
112315
|
+
if (tlsCheckWasOff) delete process.env.NODE_TLS_REJECT_UNAUTHORIZED;
|
|
111668
112316
|
host = process.env.ENGINE_HOST ?? "127.0.0.1";
|
|
111669
112317
|
port = Number(process.env.ENGINE_PORT ?? 4100);
|
|
111670
112318
|
history = new HistoryStore();
|
|
@@ -111696,6 +112344,7 @@ var init_index = __esm({
|
|
|
111696
112344
|
const url2 = `http://${host}:${typeof address === "object" && address ? address.port : port}`;
|
|
111697
112345
|
listeningUrl = url2;
|
|
111698
112346
|
app.log.info(`engine listening on ${url2}`);
|
|
112347
|
+
if (tlsCheckWasOff) app.log.warn("NODE_TLS_REJECT_UNAUTHORIZED=0 is set on this computer and was ignored: certificates are checked against the system's trusted certificates; use Skip TLS certificate check on a request to turn it off there");
|
|
111699
112348
|
if (!process.env.ATC_NO_READY_LINE) console.log(`ATC_READY ${url2}`);
|
|
111700
112349
|
} catch (err) {
|
|
111701
112350
|
app.log.error(err);
|
|
@@ -111709,7 +112358,8 @@ init_src();
|
|
|
111709
112358
|
init_draft();
|
|
111710
112359
|
init_history_store();
|
|
111711
112360
|
init_k6();
|
|
111712
|
-
import { spawn as spawn3 } from "node:child_process";
|
|
112361
|
+
import { spawn as spawn3, spawnSync } from "node:child_process";
|
|
112362
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
111713
112363
|
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
111714
112364
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync as readdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
111715
112365
|
import { readFile as readFile10, writeFile as writeFile9 } from "node:fs/promises";
|
|
@@ -111794,14 +112444,16 @@ init_secret_store();
|
|
|
111794
112444
|
init_suite_run();
|
|
111795
112445
|
init_oauth();
|
|
111796
112446
|
init_certificates();
|
|
112447
|
+
init_cookies();
|
|
111797
112448
|
init_workspace_store();
|
|
111798
112449
|
var repoRoot = resolve4(dirname3(fileURLToPath2(import.meta.url)), "../../..");
|
|
111799
112450
|
var EXIT = { passed: 0, failed: 1, error: 2 };
|
|
111800
|
-
var VERSION = "0.
|
|
112451
|
+
var VERSION = "0.9.0";
|
|
111801
112452
|
var HELP = `APITestCenter CLI ${VERSION} \u2014 https://apitestcenter.com/docs/cli-and-ci.html
|
|
111802
112453
|
|
|
111803
112454
|
Usage (apitestcenter, or the short name atc once installed)
|
|
111804
|
-
atc ui [collection] Open the app in
|
|
112455
|
+
atc ui [collection] Open the app in a window to build and edit tests
|
|
112456
|
+
(--browser: a tab in your usual browser; --no-open: just print the address)
|
|
111805
112457
|
atc init [collection] Start api-tests/ in this repository, with a CI pipeline
|
|
111806
112458
|
atc suite <collection> [options] Send every request in a collection once and check each one
|
|
111807
112459
|
atc run <request> [options] Run a load test and exit 0 (passed) / 1 (failed) / 2 (error)
|
|
@@ -111886,7 +112538,8 @@ async function main() {
|
|
|
111886
112538
|
help: { type: "boolean", short: "h" },
|
|
111887
112539
|
version: { type: "boolean", short: "v" },
|
|
111888
112540
|
data: { type: "string" },
|
|
111889
|
-
"no-open": { type: "boolean" }
|
|
112541
|
+
"no-open": { type: "boolean" },
|
|
112542
|
+
browser: { type: "boolean" }
|
|
111890
112543
|
}
|
|
111891
112544
|
});
|
|
111892
112545
|
const [command, target] = positionals;
|
|
@@ -111904,11 +112557,12 @@ async function main() {
|
|
|
111904
112557
|
}
|
|
111905
112558
|
if (command === "init") return init(target, values.data ?? "api-tests");
|
|
111906
112559
|
const dataDir = resolveDataDir();
|
|
111907
|
-
if (command === "ui") return ui(target, dataDir, values["no-open"] === true);
|
|
112560
|
+
if (command === "ui") return ui(target, dataDir, values["no-open"] === true, values.browser === true);
|
|
111908
112561
|
const secrets2 = new SecretStore(dataDir);
|
|
111909
112562
|
const workspace = new WorkspaceStore(dataDir, secrets2);
|
|
111910
112563
|
useOAuthStore(secrets2);
|
|
111911
112564
|
useCertificateStore(dataDir, secrets2);
|
|
112565
|
+
useCookieStore(secrets2);
|
|
111912
112566
|
for (const spec of values.cert ?? []) {
|
|
111913
112567
|
const eq = spec.indexOf("=");
|
|
111914
112568
|
const files = eq > 0 ? spec.slice(eq + 1).split(",").map((f) => f.trim()).filter(Boolean) : [];
|
|
@@ -111967,7 +112621,7 @@ Environments (${index.environments.length})`);
|
|
|
111967
112621
|
const message = err instanceof Error ? err.message : String(err);
|
|
111968
112622
|
fail(message.replace(/needs an environment.*$/, "is not set \u2014 use --env <name> or --var name=value"));
|
|
111969
112623
|
}
|
|
111970
|
-
let
|
|
112624
|
+
let load3 = values.preset ? applyPreset(DEFAULT_LOAD, values.preset) : { ...DEFAULT_LOAD };
|
|
111971
112625
|
const num3 = (v, label) => {
|
|
111972
112626
|
if (v == null) return void 0;
|
|
111973
112627
|
const n = Number(v);
|
|
@@ -111984,8 +112638,8 @@ Environments (${index.environments.length})`);
|
|
|
111984
112638
|
};
|
|
111985
112639
|
if (custom2.rps != null) custom2.mode = "rate";
|
|
111986
112640
|
const overrides = Object.fromEntries(Object.entries(custom2).filter(([, v]) => v !== void 0));
|
|
111987
|
-
if (Object.keys(overrides).length)
|
|
111988
|
-
const problem = loadProblem(
|
|
112641
|
+
if (Object.keys(overrides).length) load3 = { ...load3, ...overrides, stages: load3.stages && !values.duration ? load3.stages : null, preset: "custom" };
|
|
112642
|
+
const problem = loadProblem(load3);
|
|
111989
112643
|
if (problem) fail(problem);
|
|
111990
112644
|
const pct3 = (v) => v == null ? void 0 : (num3(v, "percent") ?? 0) / 100;
|
|
111991
112645
|
const targets = values.p95 || values.p99 || values.errors || values.checks ? { p95Ms: num3(values.p95, "--p95"), p99Ms: num3(values.p99, "--p99"), errorRate: pct3(values.errors), checksRate: pct3(values.checks), abortOnFail: values.abort === true } : void 0;
|
|
@@ -112005,7 +112659,7 @@ Environments (${index.environments.length})`);
|
|
|
112005
112659
|
if (!Number.isFinite(n) || n < 0) fail("--max-slower must be a percentage, like 10");
|
|
112006
112660
|
return n / 100;
|
|
112007
112661
|
})();
|
|
112008
|
-
const payload = buildPayload(draft, toLoadConfig(
|
|
112662
|
+
const payload = buildPayload(draft, toLoadConfig(load3), `${name} \xB7 CLI`, targets);
|
|
112009
112663
|
const log = (line) => !values.quiet && console.log(line);
|
|
112010
112664
|
if (draft.before) {
|
|
112011
112665
|
const before = draft.before;
|
|
@@ -112036,7 +112690,7 @@ Environments (${index.environments.length})`);
|
|
|
112036
112690
|
}
|
|
112037
112691
|
}
|
|
112038
112692
|
const started = await startRun(labRunSchema.parse(payload), repoRoot).catch((err) => fail(err instanceof Error ? err.message : String(err)));
|
|
112039
|
-
log(`load ${payload.request.method} ${displayTemplate(payload.request.url)} \xB7 ${
|
|
112693
|
+
log(`load ${payload.request.method} ${displayTemplate(payload.request.url)} \xB7 ${load3.mode === "rate" ? `${load3.rps} req/s` : `${load3.vus} VUs`} \xB7 ${load3.duration}`);
|
|
112040
112694
|
let interrupted = false;
|
|
112041
112695
|
process.on("SIGINT", () => {
|
|
112042
112696
|
if (interrupted) process.exit(EXIT.error);
|
|
@@ -112087,9 +112741,9 @@ compared with baseline \u201C${baseline.testName}\u201D (${baseline.startedAt.sl
|
|
|
112087
112741
|
if (history2) log(` saved to History in ${dataDir}`);
|
|
112088
112742
|
return failed ? EXIT.failed : EXIT.passed;
|
|
112089
112743
|
}
|
|
112090
|
-
async function ui(collection, dataDir, noOpen) {
|
|
112744
|
+
async function ui(collection, dataDir, noOpen, inBrowserTab = false) {
|
|
112091
112745
|
const here = dirname3(fileURLToPath2(import.meta.url));
|
|
112092
|
-
const uiDir2 = [resolve4(here, "ui"), resolve4(repoRoot, "apps/web/.next-desktop")].find((d) => existsSync2(resolve4(d, "index.html")));
|
|
112746
|
+
const uiDir2 = [process.env.ATC_UI_DIR, resolve4(here, "ui"), resolve4(repoRoot, "apps/web/.next-desktop")].find((d) => Boolean(d) && existsSync2(resolve4(d, "index.html")));
|
|
112093
112747
|
if (!uiDir2) fail("this build has no app screens. Use the npm package (npx apitestcenter ui), or run: pnpm --filter @apitestcenter/desktop build:ui");
|
|
112094
112748
|
Object.assign(process.env, {
|
|
112095
112749
|
ATC_UI_DIR: uiDir2,
|
|
@@ -112100,7 +112754,9 @@ async function ui(collection, dataDir, noOpen) {
|
|
|
112100
112754
|
ATC_USAGE_URL: "",
|
|
112101
112755
|
ATC_NO_DOTENV: "1",
|
|
112102
112756
|
ATC_NO_READY_LINE: "1",
|
|
112103
|
-
ATC_LOG_LEVEL: process.env.ATC_LOG_LEVEL ?? "warn"
|
|
112757
|
+
ATC_LOG_LEVEL: process.env.ATC_LOG_LEVEL ?? "warn",
|
|
112758
|
+
// The screens show "stopped" when this engine goes away (a browser tab can't be closed for them).
|
|
112759
|
+
ATC_HOST: "cli"
|
|
112104
112760
|
});
|
|
112105
112761
|
if (!(await resolveK6Bin(repoRoot)).available) {
|
|
112106
112762
|
const cached3 = cachedK6();
|
|
@@ -112115,10 +112771,54 @@ async function ui(collection, dataDir, noOpen) {
|
|
|
112115
112771
|
console.log(` tests in: ${dataDir}`);
|
|
112116
112772
|
console.log(" Saved secrets last until you stop it \u2014 use {{variables}} for tests you commit.");
|
|
112117
112773
|
console.log(" Press Ctrl+C to stop.");
|
|
112118
|
-
if (!noOpen)
|
|
112774
|
+
if (!noOpen) {
|
|
112775
|
+
const win = inBrowserTab ? null : openAppWindow(page2);
|
|
112776
|
+
if (!win) openBrowser(page2);
|
|
112777
|
+
else {
|
|
112778
|
+
console.log(" Closing the window stops it too.");
|
|
112779
|
+
const stop = (why) => {
|
|
112780
|
+
if (why) console.log(why);
|
|
112781
|
+
closeWindow(win);
|
|
112782
|
+
process.exit(EXIT.passed);
|
|
112783
|
+
};
|
|
112784
|
+
process.on("SIGINT", () => stop("\nStopped."));
|
|
112785
|
+
process.on("SIGTERM", () => stop(""));
|
|
112786
|
+
win.on("exit", () => {
|
|
112787
|
+
console.log("Window closed \u2014 stopped.");
|
|
112788
|
+
process.exit(EXIT.passed);
|
|
112789
|
+
});
|
|
112790
|
+
}
|
|
112791
|
+
}
|
|
112119
112792
|
return new Promise(() => {
|
|
112120
112793
|
});
|
|
112121
112794
|
}
|
|
112795
|
+
function findAppBrowser() {
|
|
112796
|
+
const env = process.env;
|
|
112797
|
+
const candidates = process.platform === "win32" ? [env["ProgramFiles(x86)"], env.ProgramFiles, env.LOCALAPPDATA].flatMap(
|
|
112798
|
+
(root2) => root2 ? [`${root2}\\Microsoft\\Edge\\Application\\msedge.exe`, `${root2}\\Google\\Chrome\\Application\\chrome.exe`, `${root2}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe`] : []
|
|
112799
|
+
) : process.platform === "darwin" ? ["/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge", "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "/Applications/Chromium.app/Contents/MacOS/Chromium", "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"] : ["/usr/bin/microsoft-edge", "/usr/bin/google-chrome", "/usr/bin/google-chrome-stable", "/usr/bin/chromium", "/usr/bin/chromium-browser", "/usr/bin/brave-browser", "/snap/bin/chromium"];
|
|
112800
|
+
return candidates.find((p) => existsSync2(p)) ?? null;
|
|
112801
|
+
}
|
|
112802
|
+
function openAppWindow(url2) {
|
|
112803
|
+
const bin = findAppBrowser();
|
|
112804
|
+
if (!bin) return null;
|
|
112805
|
+
const profile = resolve4(tmpdir3(), "apitestcenter-ui-window");
|
|
112806
|
+
try {
|
|
112807
|
+
const child = spawn3(bin, [`--app=${url2}`, `--user-data-dir=${profile}`, "--no-first-run", "--no-default-browser-check", "--window-size=1440,900"], { stdio: "ignore", windowsHide: false });
|
|
112808
|
+
child.on("error", () => openBrowser(url2));
|
|
112809
|
+
return child;
|
|
112810
|
+
} catch {
|
|
112811
|
+
return null;
|
|
112812
|
+
}
|
|
112813
|
+
}
|
|
112814
|
+
function closeWindow(win) {
|
|
112815
|
+
if (win.exitCode !== null || !win.pid) return;
|
|
112816
|
+
try {
|
|
112817
|
+
if (process.platform === "win32") spawnSync("taskkill", ["/pid", String(win.pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
|
|
112818
|
+
else win.kill("SIGTERM");
|
|
112819
|
+
} catch {
|
|
112820
|
+
}
|
|
112821
|
+
}
|
|
112122
112822
|
function openBrowser(url2) {
|
|
112123
112823
|
const [cmd, args] = process.platform === "win32" ? ["cmd", ["/c", "start", "", url2]] : process.platform === "darwin" ? ["open", [url2]] : ["xdg-open", [url2]];
|
|
112124
112824
|
try {
|
|
@@ -112134,9 +112834,9 @@ async function init(collection, folder) {
|
|
|
112134
112834
|
fail(`${folder} already has saved requests. Open them with: npx apitestcenter ui --data ${folder}`);
|
|
112135
112835
|
}
|
|
112136
112836
|
mkdirSync2(dir2, { recursive: true });
|
|
112137
|
-
const
|
|
112138
|
-
await
|
|
112139
|
-
await
|
|
112837
|
+
const store3 = new WorkspaceStore(dir2, new SecretStore(dir2));
|
|
112838
|
+
await store3.saveEnvironments([{ id: randomUUID7(), name: "Local", variables: [{ key: "baseUrl", value: "http://localhost:3000", secret: false }] }]);
|
|
112839
|
+
await store3.saveRequest({
|
|
112140
112840
|
name: "Health check",
|
|
112141
112841
|
collection: name,
|
|
112142
112842
|
request: { method: "GET", url: "{{baseUrl}}/health", checks: [{ type: "status", expect: "2xx" }] }
|