apitestcenter 0.8.0 → 0.10.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 +677 -180
- package/dist/ui/404/index.html +1 -1
- package/dist/ui/404.html +1 -1
- package/dist/ui/__next.__PAGE__.txt +15 -14
- package/dist/ui/__next._full.txt +13 -12
- package/dist/ui/__next._tree.txt +2 -2
- package/dist/ui/_next/static/chunks/0f9cyrqzolhzx.js +18 -0
- package/dist/ui/_next/static/chunks/0imlzn2evcfw6.css +3 -0
- package/dist/ui/_next/static/chunks/{11nxnz4doxir5.js → 1-ogjtk9dipfq.js} +319 -133
- package/dist/ui/_next/static/chunks/{2idsynwg5lkk7.js → 115l_disvbdxa.js} +1 -1
- package/dist/ui/_next/static/chunks/1b31b8g5_5h-y.js +2 -0
- package/dist/ui/_next/static/chunks/2qchu_7euauai.js +1 -0
- package/dist/ui/_next/static/chunks/{3ad2zh-p8_xhv.js → 2sa_fr4vgkkrp.js} +2 -2
- package/dist/ui/_next/static/chunks/{02myfb0nlri-z.js → 3jywp_61ylz0z.js} +1 -1
- package/dist/ui/_next/static/chunks/3qxq1d9u_kohv.js +1 -0
- package/dist/ui/_next/static/media/icon.1oy4kb0w-k_2v.png +0 -0
- package/dist/ui/_not-found/__next._full.txt +12 -11
- package/dist/ui/_not-found/__next._not-found.__PAGE__.txt +17 -16
- 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 +12 -11
- package/dist/ui/compare/__next._full.txt +13 -12
- package/dist/ui/compare/__next._tree.txt +2 -2
- package/dist/ui/compare/__next.compare.__PAGE__.txt +18 -17
- package/dist/ui/compare/index.html +1 -1
- package/dist/ui/compare/index.txt +13 -12
- package/dist/ui/history/__next._full.txt +14 -13
- package/dist/ui/history/__next._tree.txt +2 -2
- package/dist/ui/history/__next.history.__PAGE__.txt +19 -18
- package/dist/ui/history/index.html +1 -1
- package/dist/ui/history/index.txt +14 -13
- package/dist/ui/icon.png +0 -0
- package/dist/ui/index.html +1 -1
- package/dist/ui/index.txt +13 -12
- package/dist/ui/preview/__next._full.txt +14 -13
- package/dist/ui/preview/__next._tree.txt +2 -2
- package/dist/ui/preview/__next.preview.__PAGE__.txt +19 -18
- package/dist/ui/preview/index.html +1 -1
- package/dist/ui/preview/index.txt +14 -13
- package/dist/ui/suite/__next._full.txt +13 -12
- package/dist/ui/suite/__next._tree.txt +2 -2
- package/dist/ui/suite/__next.suite.__PAGE__.txt +18 -17
- package/dist/ui/suite/index.html +1 -1
- package/dist/ui/suite/index.txt +13 -12
- package/dist/ui/tests/__next._full.txt +12 -11
- package/dist/ui/tests/__next._tree.txt +2 -2
- package/dist/ui/tests/__next.tests.__PAGE__.txt +17 -16
- package/dist/ui/tests/index.html +1 -1
- package/dist/ui/tests/index.txt +12 -11
- package/package.json +1 -1
- package/dist/ui/_next/static/chunks/0-898cb98enjv.css +0 -3
- package/dist/ui/_next/static/chunks/0e1nwxi9i1ag7.js +0 -1
- package/dist/ui/_next/static/chunks/0vkcbl8nakdh1.js +0 -2
- package/dist/ui/_next/static/chunks/2nxnxp6p-40kg.js +0 -1
- package/dist/ui/_next/static/chunks/2xls_z5abvl-5.js +0 -1
- /package/dist/ui/_next/static/{BJz1RrA7HqZwUV22DBTCg → 9enzqastnBYCi6FVPT4mz}/_buildManifest.js +0 -0
- /package/dist/ui/_next/static/{BJz1RrA7HqZwUV22DBTCg → 9enzqastnBYCi6FVPT4mz}/_clientMiddlewareManifest.js +0 -0
- /package/dist/ui/_next/static/{BJz1RrA7HqZwUV22DBTCg → 9enzqastnBYCi6FVPT4mz}/_ssgManifest.js +0 -0
package/dist/cli.mjs
CHANGED
|
@@ -231,15 +231,15 @@ function normalizeView(raw) {
|
|
|
231
231
|
const v = raw;
|
|
232
232
|
if (typeof v.id !== "string" || !v.id || typeof v.name !== "string") return null;
|
|
233
233
|
const scope = v.scope === "compare" ? "compare" : "run";
|
|
234
|
-
const
|
|
234
|
+
const seen2 = /* @__PURE__ */ new Set();
|
|
235
235
|
const widgets = [];
|
|
236
236
|
for (const item of Array.isArray(v.widgets) ? v.widgets : []) {
|
|
237
237
|
const w = item;
|
|
238
238
|
const def = METRICS[w.metric];
|
|
239
239
|
if (!def || scope === "compare" && !def.compare) continue;
|
|
240
240
|
let id = typeof w.id === "string" && w.id ? w.id : `${def.id}-${widgets.length}`;
|
|
241
|
-
while (
|
|
242
|
-
|
|
241
|
+
while (seen2.has(id)) id = `${id}-x`;
|
|
242
|
+
seen2.add(id);
|
|
243
243
|
const kind = def.kinds.includes(w.kind) ? w.kind : def.kinds[0];
|
|
244
244
|
const width = clamp(w.w ?? def.size.w, def.min.w, GRID_COLUMNS);
|
|
245
245
|
widgets.push({
|
|
@@ -781,12 +781,12 @@ function variablesOf(env) {
|
|
|
781
781
|
}
|
|
782
782
|
function normalizeEnvironments(raw) {
|
|
783
783
|
if (!Array.isArray(raw)) return [];
|
|
784
|
-
const
|
|
784
|
+
const seen2 = /* @__PURE__ */ new Set();
|
|
785
785
|
const out = [];
|
|
786
786
|
for (const item of raw) {
|
|
787
787
|
const e = item;
|
|
788
|
-
if (typeof e?.id !== "string" || !e.id ||
|
|
789
|
-
|
|
788
|
+
if (typeof e?.id !== "string" || !e.id || seen2.has(e.id)) continue;
|
|
789
|
+
seen2.add(e.id);
|
|
790
790
|
const keys = /* @__PURE__ */ new Set();
|
|
791
791
|
const variables = [];
|
|
792
792
|
for (const v of Array.isArray(e.variables) ? e.variables : []) {
|
|
@@ -12334,14 +12334,14 @@ function initializeContext(params) {
|
|
|
12334
12334
|
function process2(schema, ctx, _params = { path: [], schemaPath: [] }) {
|
|
12335
12335
|
var _a3;
|
|
12336
12336
|
const def = schema._zod.def;
|
|
12337
|
-
const
|
|
12338
|
-
if (
|
|
12339
|
-
|
|
12337
|
+
const seen2 = ctx.seen.get(schema);
|
|
12338
|
+
if (seen2) {
|
|
12339
|
+
seen2.count++;
|
|
12340
12340
|
const isCycle = _params.schemaPath.includes(schema);
|
|
12341
12341
|
if (isCycle) {
|
|
12342
|
-
|
|
12342
|
+
seen2.cycle = _params.path;
|
|
12343
12343
|
}
|
|
12344
|
-
return
|
|
12344
|
+
return seen2.schema;
|
|
12345
12345
|
}
|
|
12346
12346
|
const result = { schema: {}, count: 1, cycle: void 0, path: _params.path };
|
|
12347
12347
|
ctx.seen.set(schema, result);
|
|
@@ -12424,12 +12424,12 @@ function extractDefs(ctx, schema) {
|
|
|
12424
12424
|
if (entry[1].schema.$ref) {
|
|
12425
12425
|
return;
|
|
12426
12426
|
}
|
|
12427
|
-
const
|
|
12427
|
+
const seen2 = entry[1];
|
|
12428
12428
|
const { ref, defId } = makeURI(entry);
|
|
12429
|
-
|
|
12429
|
+
seen2.def = { ...seen2.schema };
|
|
12430
12430
|
if (defId)
|
|
12431
|
-
|
|
12432
|
-
const schema2 =
|
|
12431
|
+
seen2.defId = defId;
|
|
12432
|
+
const schema2 = seen2.schema;
|
|
12433
12433
|
for (const key in schema2) {
|
|
12434
12434
|
delete schema2[key];
|
|
12435
12435
|
}
|
|
@@ -12437,16 +12437,16 @@ function extractDefs(ctx, schema) {
|
|
|
12437
12437
|
};
|
|
12438
12438
|
if (ctx.cycles === "throw") {
|
|
12439
12439
|
for (const entry of ctx.seen.entries()) {
|
|
12440
|
-
const
|
|
12441
|
-
if (
|
|
12442
|
-
throw new Error(`Cycle detected: #/${
|
|
12440
|
+
const seen2 = entry[1];
|
|
12441
|
+
if (seen2.cycle) {
|
|
12442
|
+
throw new Error(`Cycle detected: #/${seen2.cycle?.join("/")}/<root>
|
|
12443
12443
|
|
|
12444
12444
|
Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.`);
|
|
12445
12445
|
}
|
|
12446
12446
|
}
|
|
12447
12447
|
}
|
|
12448
12448
|
for (const entry of ctx.seen.entries()) {
|
|
12449
|
-
const
|
|
12449
|
+
const seen2 = entry[1];
|
|
12450
12450
|
if (schema === entry[0]) {
|
|
12451
12451
|
extractToDef(entry);
|
|
12452
12452
|
continue;
|
|
@@ -12463,11 +12463,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
12463
12463
|
extractToDef(entry);
|
|
12464
12464
|
continue;
|
|
12465
12465
|
}
|
|
12466
|
-
if (
|
|
12466
|
+
if (seen2.cycle) {
|
|
12467
12467
|
extractToDef(entry);
|
|
12468
12468
|
continue;
|
|
12469
12469
|
}
|
|
12470
|
-
if (
|
|
12470
|
+
if (seen2.count > 1) {
|
|
12471
12471
|
if (ctx.reused === "ref") {
|
|
12472
12472
|
extractToDef(entry);
|
|
12473
12473
|
continue;
|
|
@@ -12480,13 +12480,13 @@ function finalize(ctx, schema) {
|
|
|
12480
12480
|
if (!root2)
|
|
12481
12481
|
throw new Error("Unprocessed schema. This is a bug in Zod.");
|
|
12482
12482
|
const flattenRef = (zodSchema) => {
|
|
12483
|
-
const
|
|
12484
|
-
if (
|
|
12483
|
+
const seen2 = ctx.seen.get(zodSchema);
|
|
12484
|
+
if (seen2.ref === null)
|
|
12485
12485
|
return;
|
|
12486
|
-
const schema2 =
|
|
12486
|
+
const schema2 = seen2.def ?? seen2.schema;
|
|
12487
12487
|
const _cached = { ...schema2 };
|
|
12488
|
-
const ref =
|
|
12489
|
-
|
|
12488
|
+
const ref = seen2.ref;
|
|
12489
|
+
seen2.ref = null;
|
|
12490
12490
|
if (ref) {
|
|
12491
12491
|
flattenRef(ref);
|
|
12492
12492
|
const refSeen = ctx.seen.get(ref);
|
|
@@ -12538,7 +12538,7 @@ function finalize(ctx, schema) {
|
|
|
12538
12538
|
ctx.override({
|
|
12539
12539
|
zodSchema,
|
|
12540
12540
|
jsonSchema: schema2,
|
|
12541
|
-
path:
|
|
12541
|
+
path: seen2.path ?? []
|
|
12542
12542
|
});
|
|
12543
12543
|
};
|
|
12544
12544
|
for (const entry of [...ctx.seen.entries()].reverse()) {
|
|
@@ -12566,11 +12566,11 @@ function finalize(ctx, schema) {
|
|
|
12566
12566
|
delete result.id;
|
|
12567
12567
|
const defs = ctx.external?.defs ?? {};
|
|
12568
12568
|
for (const entry of ctx.seen.entries()) {
|
|
12569
|
-
const
|
|
12570
|
-
if (
|
|
12571
|
-
if (
|
|
12572
|
-
delete
|
|
12573
|
-
defs[
|
|
12569
|
+
const seen2 = entry[1];
|
|
12570
|
+
if (seen2.def && seen2.defId) {
|
|
12571
|
+
if (seen2.def.id === seen2.defId)
|
|
12572
|
+
delete seen2.def.id;
|
|
12573
|
+
defs[seen2.defId] = seen2.def;
|
|
12574
12574
|
}
|
|
12575
12575
|
}
|
|
12576
12576
|
if (ctx.external) {
|
|
@@ -13106,9 +13106,9 @@ var init_json_schema_processors = __esm({
|
|
|
13106
13106
|
nullableProcessor = (schema, ctx, json2, params) => {
|
|
13107
13107
|
const def = schema._zod.def;
|
|
13108
13108
|
const inner = process2(def.innerType, ctx, params);
|
|
13109
|
-
const
|
|
13109
|
+
const seen2 = ctx.seen.get(schema);
|
|
13110
13110
|
if (ctx.target === "openapi-3.0") {
|
|
13111
|
-
|
|
13111
|
+
seen2.ref = def.innerType;
|
|
13112
13112
|
json2.nullable = true;
|
|
13113
13113
|
} else {
|
|
13114
13114
|
json2.anyOf = [inner, { type: "null" }];
|
|
@@ -13117,29 +13117,29 @@ var init_json_schema_processors = __esm({
|
|
|
13117
13117
|
nonoptionalProcessor = (schema, ctx, _json, params) => {
|
|
13118
13118
|
const def = schema._zod.def;
|
|
13119
13119
|
process2(def.innerType, ctx, params);
|
|
13120
|
-
const
|
|
13121
|
-
|
|
13120
|
+
const seen2 = ctx.seen.get(schema);
|
|
13121
|
+
seen2.ref = def.innerType;
|
|
13122
13122
|
};
|
|
13123
13123
|
defaultProcessor = (schema, ctx, json2, params) => {
|
|
13124
13124
|
const def = schema._zod.def;
|
|
13125
13125
|
process2(def.innerType, ctx, params);
|
|
13126
|
-
const
|
|
13127
|
-
|
|
13126
|
+
const seen2 = ctx.seen.get(schema);
|
|
13127
|
+
seen2.ref = def.innerType;
|
|
13128
13128
|
json2.default = JSON.parse(JSON.stringify(def.defaultValue));
|
|
13129
13129
|
};
|
|
13130
13130
|
prefaultProcessor = (schema, ctx, json2, params) => {
|
|
13131
13131
|
const def = schema._zod.def;
|
|
13132
13132
|
process2(def.innerType, ctx, params);
|
|
13133
|
-
const
|
|
13134
|
-
|
|
13133
|
+
const seen2 = ctx.seen.get(schema);
|
|
13134
|
+
seen2.ref = def.innerType;
|
|
13135
13135
|
if (ctx.io === "input")
|
|
13136
13136
|
json2._prefault = JSON.parse(JSON.stringify(def.defaultValue));
|
|
13137
13137
|
};
|
|
13138
13138
|
catchProcessor = (schema, ctx, json2, params) => {
|
|
13139
13139
|
const def = schema._zod.def;
|
|
13140
13140
|
process2(def.innerType, ctx, params);
|
|
13141
|
-
const
|
|
13142
|
-
|
|
13141
|
+
const seen2 = ctx.seen.get(schema);
|
|
13142
|
+
seen2.ref = def.innerType;
|
|
13143
13143
|
let catchValue;
|
|
13144
13144
|
try {
|
|
13145
13145
|
catchValue = def.catchValue(void 0);
|
|
@@ -13153,33 +13153,33 @@ var init_json_schema_processors = __esm({
|
|
|
13153
13153
|
const inIsTransform = def.in._zod.traits.has("$ZodTransform");
|
|
13154
13154
|
const innerType = ctx.io === "input" ? inIsTransform ? def.out : def.in : def.out;
|
|
13155
13155
|
process2(innerType, ctx, params);
|
|
13156
|
-
const
|
|
13157
|
-
|
|
13156
|
+
const seen2 = ctx.seen.get(schema);
|
|
13157
|
+
seen2.ref = innerType;
|
|
13158
13158
|
};
|
|
13159
13159
|
readonlyProcessor = (schema, ctx, json2, params) => {
|
|
13160
13160
|
const def = schema._zod.def;
|
|
13161
13161
|
process2(def.innerType, ctx, params);
|
|
13162
|
-
const
|
|
13163
|
-
|
|
13162
|
+
const seen2 = ctx.seen.get(schema);
|
|
13163
|
+
seen2.ref = def.innerType;
|
|
13164
13164
|
json2.readOnly = true;
|
|
13165
13165
|
};
|
|
13166
13166
|
promiseProcessor = (schema, ctx, _json, params) => {
|
|
13167
13167
|
const def = schema._zod.def;
|
|
13168
13168
|
process2(def.innerType, ctx, params);
|
|
13169
|
-
const
|
|
13170
|
-
|
|
13169
|
+
const seen2 = ctx.seen.get(schema);
|
|
13170
|
+
seen2.ref = def.innerType;
|
|
13171
13171
|
};
|
|
13172
13172
|
optionalProcessor = (schema, ctx, _json, params) => {
|
|
13173
13173
|
const def = schema._zod.def;
|
|
13174
13174
|
process2(def.innerType, ctx, params);
|
|
13175
|
-
const
|
|
13176
|
-
|
|
13175
|
+
const seen2 = ctx.seen.get(schema);
|
|
13176
|
+
seen2.ref = def.innerType;
|
|
13177
13177
|
};
|
|
13178
13178
|
lazyProcessor = (schema, ctx, _json, params) => {
|
|
13179
13179
|
const innerType = schema._zod.innerType;
|
|
13180
13180
|
process2(innerType, ctx, params);
|
|
13181
|
-
const
|
|
13182
|
-
|
|
13181
|
+
const seen2 = ctx.seen.get(schema);
|
|
13182
|
+
seen2.ref = innerType;
|
|
13183
13183
|
};
|
|
13184
13184
|
allProcessors = {
|
|
13185
13185
|
string: stringProcessor,
|
|
@@ -16068,13 +16068,41 @@ function checkLabel(check2) {
|
|
|
16068
16068
|
case "status":
|
|
16069
16069
|
return `status is ${check2.expect.replace(/\s+/g, "")}`;
|
|
16070
16070
|
case "body":
|
|
16071
|
+
if (check2.op === "is_json") return "body is JSON";
|
|
16072
|
+
if (check2.op === "matches") return `body matches /${clip(check2.value)}/`;
|
|
16071
16073
|
return `body ${check2.op === "contains" ? "contains" : "does not contain"} "${clip(check2.value)}"`;
|
|
16072
|
-
case "json":
|
|
16073
|
-
|
|
16074
|
+
case "json": {
|
|
16075
|
+
const path = jsonPathLabel(check2.path);
|
|
16076
|
+
switch (check2.op) {
|
|
16077
|
+
case "exists":
|
|
16078
|
+
return `${path} exists`;
|
|
16079
|
+
case "not_exists":
|
|
16080
|
+
return `${path} does not exist`;
|
|
16081
|
+
case "greater":
|
|
16082
|
+
return `${path} > ${clip(check2.value)}`;
|
|
16083
|
+
case "less":
|
|
16084
|
+
return `${path} < ${clip(check2.value)}`;
|
|
16085
|
+
case "matches":
|
|
16086
|
+
return `${path} matches /${clip(check2.value)}/`;
|
|
16087
|
+
case "type":
|
|
16088
|
+
return `${path} is ${/^[aeiou]/.test(check2.value) ? "an" : "a"} ${check2.value || "?"}`;
|
|
16089
|
+
case "length":
|
|
16090
|
+
return `length of ${path} ${lengthRule(check2.value).label}`;
|
|
16091
|
+
default:
|
|
16092
|
+
return `${path} ${check2.op === "equals" ? "=" : check2.op === "not_equals" ? "\u2260" : "contains"} "${clip(check2.value)}"`;
|
|
16093
|
+
}
|
|
16094
|
+
}
|
|
16074
16095
|
case "header":
|
|
16075
|
-
|
|
16096
|
+
if (check2.op === "exists") return `header ${check2.name} exists`;
|
|
16097
|
+
if (check2.op === "not_exists") return `header ${check2.name} does not exist`;
|
|
16098
|
+
if (check2.op === "matches") return `header ${check2.name} matches /${clip(check2.value)}/`;
|
|
16099
|
+
return `header ${check2.name} ${check2.op === "equals" ? "=" : "contains"} "${clip(check2.value)}"`;
|
|
16076
16100
|
case "time":
|
|
16077
16101
|
return `responds within ${check2.maxMs} ms`;
|
|
16102
|
+
case "schema": {
|
|
16103
|
+
const title = schemaTitle(check2.schema);
|
|
16104
|
+
return title ? `body matches schema "${clip(title)}"` : "body matches the JSON schema";
|
|
16105
|
+
}
|
|
16078
16106
|
}
|
|
16079
16107
|
}
|
|
16080
16108
|
function clip(text) {
|
|
@@ -16082,26 +16110,86 @@ function clip(text) {
|
|
|
16082
16110
|
}
|
|
16083
16111
|
function jsonPathLabel(path) {
|
|
16084
16112
|
const trimmed = path.trim().replace(/^\$\.?/, "");
|
|
16085
|
-
return `$.${trimmed}
|
|
16113
|
+
return trimmed ? `$.${trimmed}` : "$";
|
|
16114
|
+
}
|
|
16115
|
+
function schemaTitle(schema) {
|
|
16116
|
+
try {
|
|
16117
|
+
const parsed = JSON.parse(schema);
|
|
16118
|
+
return typeof parsed?.title === "string" && parsed.title.trim() ? parsed.title.trim() : void 0;
|
|
16119
|
+
} catch {
|
|
16120
|
+
return void 0;
|
|
16121
|
+
}
|
|
16122
|
+
}
|
|
16123
|
+
function lengthRule(value) {
|
|
16124
|
+
const m = /^\s*(>=|<=|!=|>|<|=)?\s*(\d+)\s*$/.exec(value);
|
|
16125
|
+
if (!m) return { label: `is ${value.trim() || "?"}` };
|
|
16126
|
+
const op = m[1] ?? "=";
|
|
16127
|
+
return { label: `${op === "=" ? "=" : op === "!=" ? "\u2260" : op === ">=" ? "\u2265" : op === "<=" ? "\u2264" : op} ${m[2]}` };
|
|
16086
16128
|
}
|
|
16087
16129
|
function checksNeedBody(checks) {
|
|
16088
|
-
return checks.some((c) => c.type === "body" || c.type === "json");
|
|
16130
|
+
return checks.some((c) => c.type === "body" || c.type === "json" || c.type === "schema");
|
|
16089
16131
|
}
|
|
16090
16132
|
function evaluator() {
|
|
16091
16133
|
compiled ??= new Function(`${CHECK_EVALUATOR_JS}
|
|
16092
|
-
return __atcCheck;`)();
|
|
16134
|
+
return { check: __atcCheck, schemaError: __atcSchemaError, jsonGet: __atcJsonGet };`)();
|
|
16093
16135
|
return compiled;
|
|
16094
16136
|
}
|
|
16137
|
+
function explain(check2, res) {
|
|
16138
|
+
if (check2.type === "status") return `got ${res.status}`;
|
|
16139
|
+
if (check2.type === "time") return `took ${Math.round(res.durationMs)} ms`;
|
|
16140
|
+
if (check2.type === "header") {
|
|
16141
|
+
const h = res.headers[check2.name.toLowerCase()];
|
|
16142
|
+
return h === void 0 ? "no such header" : `is "${clip(h)}"`;
|
|
16143
|
+
}
|
|
16144
|
+
if (res.body === null) return "no body";
|
|
16145
|
+
if (check2.type === "body") return check2.op === "is_json" ? "not valid JSON" : void 0;
|
|
16146
|
+
let parsed;
|
|
16147
|
+
try {
|
|
16148
|
+
parsed = JSON.parse(res.body);
|
|
16149
|
+
} catch {
|
|
16150
|
+
return "the body is not JSON";
|
|
16151
|
+
}
|
|
16152
|
+
if (check2.type === "schema") {
|
|
16153
|
+
let schema;
|
|
16154
|
+
try {
|
|
16155
|
+
schema = JSON.parse(check2.schema);
|
|
16156
|
+
} catch {
|
|
16157
|
+
return "the schema is not valid JSON";
|
|
16158
|
+
}
|
|
16159
|
+
return evaluator().schemaError(schema, parsed, "", schema, 0) ?? void 0;
|
|
16160
|
+
}
|
|
16161
|
+
if (check2.type === "json") {
|
|
16162
|
+
const got = evaluator().jsonGet(parsed, check2.path);
|
|
16163
|
+
if (!got.found) return check2.op === "not_exists" ? void 0 : "not found";
|
|
16164
|
+
const v = got.value;
|
|
16165
|
+
if (check2.op === "type") return `is ${v === null ? "null" : Array.isArray(v) ? "an array" : typeof v === "object" ? "an object" : `a ${typeof v}`}`;
|
|
16166
|
+
if (check2.op === "length") {
|
|
16167
|
+
const n = typeof v === "string" || Array.isArray(v) ? v.length : v && typeof v === "object" ? Object.keys(v).length : void 0;
|
|
16168
|
+
return n === void 0 ? "has no length" : `length is ${n}`;
|
|
16169
|
+
}
|
|
16170
|
+
if (check2.op === "not_exists") return "it exists";
|
|
16171
|
+
const text = typeof v === "string" ? v : JSON.stringify(v);
|
|
16172
|
+
return `is ${clip(text ?? "")}`;
|
|
16173
|
+
}
|
|
16174
|
+
return void 0;
|
|
16175
|
+
}
|
|
16095
16176
|
function evaluateChecks(checks, res) {
|
|
16096
|
-
const run2 = evaluator();
|
|
16177
|
+
const run2 = evaluator().check;
|
|
16097
16178
|
return checks.map((check2) => {
|
|
16098
16179
|
let ok = false;
|
|
16099
16180
|
try {
|
|
16100
|
-
ok = Boolean(run2(check2, res));
|
|
16181
|
+
ok = Boolean(run2({ ...check2 }, res));
|
|
16101
16182
|
} catch {
|
|
16102
16183
|
ok = false;
|
|
16103
16184
|
}
|
|
16104
|
-
return { name: checkLabel(check2), ok };
|
|
16185
|
+
if (ok) return { name: checkLabel(check2), ok };
|
|
16186
|
+
let detail;
|
|
16187
|
+
try {
|
|
16188
|
+
detail = explain(check2, res);
|
|
16189
|
+
} catch {
|
|
16190
|
+
detail = void 0;
|
|
16191
|
+
}
|
|
16192
|
+
return detail ? { name: checkLabel(check2), ok, detail } : { name: checkLabel(check2), ok };
|
|
16105
16193
|
});
|
|
16106
16194
|
}
|
|
16107
16195
|
var responseCheckSchema, CHECK_EVALUATOR_JS, compiled;
|
|
@@ -16111,20 +16199,28 @@ var init_checks3 = __esm({
|
|
|
16111
16199
|
init_zod();
|
|
16112
16200
|
responseCheckSchema = external_exports.discriminatedUnion("type", [
|
|
16113
16201
|
external_exports.object({ type: external_exports.literal("status"), expect: external_exports.string().min(1).max(100) }).strict(),
|
|
16114
|
-
external_exports.object({
|
|
16202
|
+
external_exports.object({
|
|
16203
|
+
type: external_exports.literal("body"),
|
|
16204
|
+
op: external_exports.enum(["contains", "not_contains", "matches", "is_json"]),
|
|
16205
|
+
// Not used by is_json.
|
|
16206
|
+
value: external_exports.string().max(2e3).default("")
|
|
16207
|
+
}).strict(),
|
|
16115
16208
|
external_exports.object({
|
|
16116
16209
|
type: external_exports.literal("json"),
|
|
16117
16210
|
path: external_exports.string().min(1).max(300),
|
|
16118
|
-
op: external_exports.enum(["exists", "equals", "not_equals", "contains"]),
|
|
16211
|
+
op: external_exports.enum(["exists", "not_exists", "equals", "not_equals", "contains", "greater", "less", "matches", "type", "length"]),
|
|
16119
16212
|
value: external_exports.string().max(2e3).default("")
|
|
16120
16213
|
}).strict(),
|
|
16121
16214
|
external_exports.object({
|
|
16122
16215
|
type: external_exports.literal("header"),
|
|
16123
16216
|
name: external_exports.string().min(1).max(200),
|
|
16124
|
-
op: external_exports.enum(["exists", "equals", "contains"]),
|
|
16217
|
+
op: external_exports.enum(["exists", "not_exists", "equals", "contains", "matches"]),
|
|
16125
16218
|
value: external_exports.string().max(2e3).default("")
|
|
16126
16219
|
}).strict(),
|
|
16127
|
-
external_exports.object({ type: external_exports.literal("time"), maxMs: external_exports.number().positive().max(6e5) }).strict()
|
|
16220
|
+
external_exports.object({ type: external_exports.literal("time"), maxMs: external_exports.number().positive().max(6e5) }).strict(),
|
|
16221
|
+
// A JSON Schema (draft 7 subset) the whole body must match.
|
|
16222
|
+
// Kept while still empty (being written); only a JSON object is sent.
|
|
16223
|
+
external_exports.object({ type: external_exports.literal("schema"), schema: external_exports.string().max(2e5) }).strict()
|
|
16128
16224
|
]);
|
|
16129
16225
|
CHECK_EVALUATOR_JS = String.raw`
|
|
16130
16226
|
function __atcStatusOk(expect, status) {
|
|
@@ -16159,27 +16255,167 @@ function __atcAsText(value) {
|
|
|
16159
16255
|
if (value === undefined) return "";
|
|
16160
16256
|
return JSON.stringify(value);
|
|
16161
16257
|
}
|
|
16258
|
+
function __atcTypeOf(value) {
|
|
16259
|
+
if (value === null) return "null";
|
|
16260
|
+
if (Array.isArray(value)) return "array";
|
|
16261
|
+
return typeof value;
|
|
16262
|
+
}
|
|
16263
|
+
function __atcIsType(value, type) {
|
|
16264
|
+
if (type === "integer") return typeof value === "number" && isFinite(value) && Math.floor(value) === value;
|
|
16265
|
+
if (type === "number") return typeof value === "number" && isFinite(value);
|
|
16266
|
+
return __atcTypeOf(value) === type;
|
|
16267
|
+
}
|
|
16268
|
+
function __atcMatches(pattern, text) {
|
|
16269
|
+
try { return new RegExp(pattern).test(text); } catch (e) { return false; }
|
|
16270
|
+
}
|
|
16271
|
+
function __atcLengthOk(rule, n) {
|
|
16272
|
+
var m = String(rule).match(/^\s*(>=|<=|!=|>|<|=)?\s*(\d+)\s*$/);
|
|
16273
|
+
if (!m) return false;
|
|
16274
|
+
var want = Number(m[2]);
|
|
16275
|
+
var op = m[1] || "=";
|
|
16276
|
+
if (op === ">") return n > want;
|
|
16277
|
+
if (op === "<") return n < want;
|
|
16278
|
+
if (op === ">=") return n >= want;
|
|
16279
|
+
if (op === "<=") return n <= want;
|
|
16280
|
+
if (op === "!=") return n !== want;
|
|
16281
|
+
return n === want;
|
|
16282
|
+
}
|
|
16283
|
+
function __atcPathText(path) { return path === "" ? "$" : "$" + path; }
|
|
16284
|
+
// JSON Schema, the parts APIs use: type (and nullable), enum, const, properties, required,
|
|
16285
|
+
// additionalProperties, items, min/max (Length, Items, imum), pattern, allOf/anyOf/oneOf,
|
|
16286
|
+
// local $ref. Returns the first problem as "$.items[0].id should be number", or null.
|
|
16287
|
+
function __atcSchemaError(schema, value, path, root, depth) {
|
|
16288
|
+
if (schema === true || schema === undefined || schema === null) return null;
|
|
16289
|
+
if (schema === false) return __atcPathText(path) + " should not be there";
|
|
16290
|
+
if (depth > 60) return null;
|
|
16291
|
+
if (typeof schema.$ref === "string") {
|
|
16292
|
+
var ref = schema.$ref.replace(/^#\/?/, "").split("/");
|
|
16293
|
+
var target = root;
|
|
16294
|
+
for (var r = 0; r < ref.length && target; r++) if (ref[r]) target = target[ref[r].replace(/~1/g, "/").replace(/~0/g, "~")];
|
|
16295
|
+
return target ? __atcSchemaError(target, value, path, root, depth + 1) : null;
|
|
16296
|
+
}
|
|
16297
|
+
var where = __atcPathText(path);
|
|
16298
|
+
if (schema.type !== undefined) {
|
|
16299
|
+
var types = Array.isArray(schema.type) ? schema.type : [schema.type];
|
|
16300
|
+
if (schema.nullable === true) types = types.concat(["null"]);
|
|
16301
|
+
var okType = false;
|
|
16302
|
+
for (var t = 0; t < types.length; t++) if (__atcIsType(value, types[t])) okType = true;
|
|
16303
|
+
if (!okType) return where + " should be " + types.join(" or ") + " (it is " + __atcTypeOf(value) + ")";
|
|
16304
|
+
}
|
|
16305
|
+
if (schema.enum && Array.isArray(schema.enum)) {
|
|
16306
|
+
var inEnum = false;
|
|
16307
|
+
for (var e = 0; e < schema.enum.length; e++) if (JSON.stringify(schema.enum[e]) === JSON.stringify(value)) inEnum = true;
|
|
16308
|
+
if (!inEnum) return where + " should be one of " + JSON.stringify(schema.enum);
|
|
16309
|
+
}
|
|
16310
|
+
if (schema.const !== undefined && JSON.stringify(schema.const) !== JSON.stringify(value)) return where + " should be " + JSON.stringify(schema.const);
|
|
16311
|
+
if (typeof value === "string") {
|
|
16312
|
+
if (typeof schema.minLength === "number" && value.length < schema.minLength) return where + " should be at least " + schema.minLength + " characters";
|
|
16313
|
+
if (typeof schema.maxLength === "number" && value.length > schema.maxLength) return where + " should be at most " + schema.maxLength + " characters";
|
|
16314
|
+
if (typeof schema.pattern === "string" && !__atcMatches(schema.pattern, value)) return where + " should match /" + schema.pattern + "/";
|
|
16315
|
+
}
|
|
16316
|
+
if (typeof value === "number") {
|
|
16317
|
+
if (typeof schema.minimum === "number" && value < schema.minimum) return where + " should be at least " + schema.minimum;
|
|
16318
|
+
if (typeof schema.maximum === "number" && value > schema.maximum) return where + " should be at most " + schema.maximum;
|
|
16319
|
+
if (typeof schema.exclusiveMinimum === "number" && value <= schema.exclusiveMinimum) return where + " should be more than " + schema.exclusiveMinimum;
|
|
16320
|
+
if (typeof schema.exclusiveMaximum === "number" && value >= schema.exclusiveMaximum) return where + " should be less than " + schema.exclusiveMaximum;
|
|
16321
|
+
}
|
|
16322
|
+
if (Array.isArray(value)) {
|
|
16323
|
+
if (typeof schema.minItems === "number" && value.length < schema.minItems) return where + " should have at least " + schema.minItems + " items";
|
|
16324
|
+
if (typeof schema.maxItems === "number" && value.length > schema.maxItems) return where + " should have at most " + schema.maxItems + " items";
|
|
16325
|
+
if (schema.items && typeof schema.items === "object" && !Array.isArray(schema.items)) {
|
|
16326
|
+
for (var i = 0; i < value.length; i++) {
|
|
16327
|
+
var itemErr = __atcSchemaError(schema.items, value[i], path + "[" + i + "]", root, depth + 1);
|
|
16328
|
+
if (itemErr) return itemErr;
|
|
16329
|
+
}
|
|
16330
|
+
}
|
|
16331
|
+
}
|
|
16332
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
16333
|
+
var req = Array.isArray(schema.required) ? schema.required : [];
|
|
16334
|
+
for (var q = 0; q < req.length; q++) if (!Object.prototype.hasOwnProperty.call(value, req[q])) return where + " should have " + req[q];
|
|
16335
|
+
var props = schema.properties && typeof schema.properties === "object" ? schema.properties : {};
|
|
16336
|
+
for (var key in value) {
|
|
16337
|
+
if (!Object.prototype.hasOwnProperty.call(value, key)) continue;
|
|
16338
|
+
var sub = path + (/^[A-Za-z_$][\w$]*$/.test(key) ? "." + key : "[" + JSON.stringify(key) + "]");
|
|
16339
|
+
if (Object.prototype.hasOwnProperty.call(props, key)) {
|
|
16340
|
+
var propErr = __atcSchemaError(props[key], value[key], sub, root, depth + 1);
|
|
16341
|
+
if (propErr) return propErr;
|
|
16342
|
+
} else if (schema.additionalProperties === false) {
|
|
16343
|
+
return __atcPathText(sub) + " is not expected";
|
|
16344
|
+
} else if (schema.additionalProperties && typeof schema.additionalProperties === "object") {
|
|
16345
|
+
var addErr = __atcSchemaError(schema.additionalProperties, value[key], sub, root, depth + 1);
|
|
16346
|
+
if (addErr) return addErr;
|
|
16347
|
+
}
|
|
16348
|
+
}
|
|
16349
|
+
}
|
|
16350
|
+
if (Array.isArray(schema.allOf)) {
|
|
16351
|
+
for (var a = 0; a < schema.allOf.length; a++) {
|
|
16352
|
+
var allErr = __atcSchemaError(schema.allOf[a], value, path, root, depth + 1);
|
|
16353
|
+
if (allErr) return allErr;
|
|
16354
|
+
}
|
|
16355
|
+
}
|
|
16356
|
+
var some = schema.anyOf || schema.oneOf;
|
|
16357
|
+
if (Array.isArray(some) && some.length) {
|
|
16358
|
+
var first = null;
|
|
16359
|
+
var matched = 0;
|
|
16360
|
+
for (var s = 0; s < some.length; s++) {
|
|
16361
|
+
var someErr = __atcSchemaError(some[s], value, path, root, depth + 1);
|
|
16362
|
+
if (!someErr) matched++;
|
|
16363
|
+
else if (!first) first = someErr;
|
|
16364
|
+
}
|
|
16365
|
+
if (!matched) return first;
|
|
16366
|
+
if (schema.oneOf && matched > 1) return where + " matches more than one of oneOf";
|
|
16367
|
+
}
|
|
16368
|
+
return null;
|
|
16369
|
+
}
|
|
16370
|
+
function __atcParsedSchema(check) {
|
|
16371
|
+
if (check.__parsed === undefined) {
|
|
16372
|
+
try { check.__parsed = JSON.parse(check.schema); } catch (e) { check.__parsed = null; }
|
|
16373
|
+
}
|
|
16374
|
+
return check.__parsed;
|
|
16375
|
+
}
|
|
16162
16376
|
function __atcCheck(check, res) {
|
|
16163
16377
|
if (check.type === "status") return __atcStatusOk(check.expect, res.status);
|
|
16164
16378
|
if (check.type === "time") return res.durationMs <= check.maxMs;
|
|
16165
16379
|
if (check.type === "header") {
|
|
16166
16380
|
var h = res.headers[String(check.name).toLowerCase()];
|
|
16167
16381
|
if (check.op === "exists") return h !== undefined;
|
|
16382
|
+
if (check.op === "not_exists") return h === undefined;
|
|
16168
16383
|
if (h === undefined) return false;
|
|
16384
|
+
if (check.op === "matches") return __atcMatches(check.value, h);
|
|
16169
16385
|
return check.op === "equals" ? h === check.value : h.indexOf(check.value) >= 0;
|
|
16170
16386
|
}
|
|
16171
16387
|
if (res.body === null || res.body === undefined) return false;
|
|
16172
16388
|
if (check.type === "body") {
|
|
16389
|
+
if (check.op === "is_json") { try { JSON.parse(res.body); return true; } catch (e) { return false; } }
|
|
16390
|
+
if (check.op === "matches") return __atcMatches(check.value, res.body);
|
|
16173
16391
|
var has = res.body.indexOf(check.value) >= 0;
|
|
16174
16392
|
return check.op === "contains" ? has : !has;
|
|
16175
16393
|
}
|
|
16394
|
+
var parsed;
|
|
16395
|
+
try { parsed = JSON.parse(res.body); } catch (e) { return false; }
|
|
16396
|
+
if (check.type === "schema") {
|
|
16397
|
+
var schema = __atcParsedSchema(check);
|
|
16398
|
+
return schema !== null && __atcSchemaError(schema, parsed, "", schema, 0) === null;
|
|
16399
|
+
}
|
|
16176
16400
|
if (check.type === "json") {
|
|
16177
|
-
var parsed;
|
|
16178
|
-
try { parsed = JSON.parse(res.body); } catch (e) { return false; }
|
|
16179
16401
|
var got = __atcJsonGet(parsed, check.path);
|
|
16180
16402
|
if (check.op === "exists") return got.found;
|
|
16403
|
+
if (check.op === "not_exists") return !got.found;
|
|
16181
16404
|
if (!got.found) return check.op === "not_equals";
|
|
16405
|
+
if (check.op === "type") return __atcIsType(got.value, String(check.value).trim().toLowerCase());
|
|
16406
|
+
if (check.op === "length") {
|
|
16407
|
+
var v = got.value;
|
|
16408
|
+
var n = typeof v === "string" || Array.isArray(v) ? v.length : v !== null && typeof v === "object" ? Object.keys(v).length : -1;
|
|
16409
|
+
return n >= 0 && __atcLengthOk(check.value, n);
|
|
16410
|
+
}
|
|
16411
|
+
if (check.op === "greater" || check.op === "less") {
|
|
16412
|
+
var num = Number(got.value);
|
|
16413
|
+
var lim = Number(check.value);
|
|
16414
|
+
if (got.value === null || got.value === "" || typeof got.value === "boolean" || !isFinite(num) || String(check.value).trim() === "" || !isFinite(lim)) return false;
|
|
16415
|
+
return check.op === "greater" ? num > lim : num < lim;
|
|
16416
|
+
}
|
|
16182
16417
|
var text = __atcAsText(got.value);
|
|
16418
|
+
if (check.op === "matches") return __atcMatches(check.value, text);
|
|
16183
16419
|
if (check.op === "equals") return text === check.value;
|
|
16184
16420
|
if (check.op === "not_equals") return text !== check.value;
|
|
16185
16421
|
return text.indexOf(check.value) >= 0;
|
|
@@ -16603,31 +16839,32 @@ var init_definition = __esm({
|
|
|
16603
16839
|
});
|
|
16604
16840
|
|
|
16605
16841
|
// ../shared/src/chain.ts
|
|
16606
|
-
function fillText(text, vars, mode = "raw") {
|
|
16842
|
+
function fillText(text, vars, mode = "raw", builtins = true) {
|
|
16607
16843
|
fillFn ??= new Function(`${FILL_JS}
|
|
16608
16844
|
return __atcFill;`)();
|
|
16609
|
-
return fillFn(text, vars, mode);
|
|
16845
|
+
return fillFn(text, vars, mode, builtins);
|
|
16610
16846
|
}
|
|
16611
|
-
function fillDeep(value, vars) {
|
|
16612
|
-
if (typeof value === "string") return fillText(value, vars, "raw");
|
|
16613
|
-
if (Array.isArray(value)) return value.map((v) => fillDeep(v, vars));
|
|
16847
|
+
function fillDeep(value, vars, builtins) {
|
|
16848
|
+
if (typeof value === "string") return fillText(value, vars, "raw", builtins);
|
|
16849
|
+
if (Array.isArray(value)) return value.map((v) => fillDeep(v, vars, builtins));
|
|
16614
16850
|
if (value && typeof value === "object") {
|
|
16615
|
-
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, fillDeep(v, vars)]));
|
|
16851
|
+
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, fillDeep(v, vars, builtins)]));
|
|
16616
16852
|
}
|
|
16617
16853
|
return value;
|
|
16618
16854
|
}
|
|
16619
|
-
function fillLab(lab, vars) {
|
|
16620
|
-
if (!Object.keys(vars).length) return lab;
|
|
16621
|
-
const raw = (s) => fillText(s, vars, "raw");
|
|
16855
|
+
function fillLab(lab, vars, { builtins = true } = {}) {
|
|
16856
|
+
if (!Object.keys(vars).length && !builtins) return lab;
|
|
16857
|
+
const raw = (s) => fillText(s, vars, "raw", builtins);
|
|
16858
|
+
const deep = (v) => fillDeep(v, vars, builtins);
|
|
16622
16859
|
const { request: request3, auth } = lab;
|
|
16623
16860
|
const body = request3.body;
|
|
16624
|
-
const filledBody = body.mode === "json" ? { ...body, json:
|
|
16861
|
+
const filledBody = body.mode === "json" ? { ...body, json: deep(body.json) } : body.mode === "text" ? { ...body, text: raw(body.text) } : body.mode === "form" ? { ...body, fields: body.fields.map((f) => ({ key: raw(f.key), value: raw(f.value) })) } : body.mode === "raw" ? { ...body, raw: raw(body.raw) } : body;
|
|
16625
16862
|
const filledAuth = auth.mode === "bearer" ? { ...auth, token: raw(auth.token) } : auth.mode === "basic" ? { ...auth, username: raw(auth.username), password: raw(auth.password) } : auth.mode === "api_key" ? { ...auth, name: raw(auth.name), value: raw(auth.value) } : auth;
|
|
16626
16863
|
return {
|
|
16627
16864
|
...lab,
|
|
16628
16865
|
request: {
|
|
16629
16866
|
...request3,
|
|
16630
|
-
url: fillText(request3.url, vars, "url"),
|
|
16867
|
+
url: fillText(request3.url, vars, "url", builtins),
|
|
16631
16868
|
query: request3.query.map((q) => ({ key: raw(q.key), value: raw(q.value) })),
|
|
16632
16869
|
headers: request3.headers.map((h) => ({ key: h.key, value: raw(h.value) })),
|
|
16633
16870
|
body: filledBody
|
|
@@ -16679,12 +16916,58 @@ var init_chain = __esm({
|
|
|
16679
16916
|
init_checks3();
|
|
16680
16917
|
init_definition();
|
|
16681
16918
|
FILL_JS = String.raw`
|
|
16682
|
-
|
|
16919
|
+
var __ATC_FIRST = ["Alex", "Maria", "Sam", "Priya", "Omar", "Lena", "Kofi", "Yuki", "Diego", "Emma", "Noah", "Aisha", "Ivan", "Sofia", "Liam", "Mei"];
|
|
16920
|
+
var __ATC_LAST = ["Smith", "Garcia", "Kim", "Patel", "Muller", "Silva", "Nguyen", "Okafor", "Rossi", "Cohen", "Tanaka", "Novak", "Brown", "Khan", "Lopez", "Berg"];
|
|
16921
|
+
function __atcRand(n) { return Math.floor(Math.random() * n); }
|
|
16922
|
+
function __atcRandText(len, chars) {
|
|
16923
|
+
var out = "";
|
|
16924
|
+
for (var i = 0; i < len; i++) out += chars.charAt(__atcRand(chars.length));
|
|
16925
|
+
return out;
|
|
16926
|
+
}
|
|
16927
|
+
function __atcBuiltin(name, arg) {
|
|
16928
|
+
var args = arg === undefined || String(arg).trim() === "" ? [] : String(arg).split(",");
|
|
16929
|
+
for (var i = 0; i < args.length; i++) args[i] = Number(String(args[i]).trim());
|
|
16930
|
+
switch (name.slice(1).toLowerCase()) {
|
|
16931
|
+
case "uuid": case "guid": case "randomuuid":
|
|
16932
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { var r = __atcRand(16); return (c === "x" ? r : (r & 3) | 8).toString(16); });
|
|
16933
|
+
case "timestamp": return String(Math.floor(Date.now() / 1000));
|
|
16934
|
+
case "timestampms": return String(Date.now());
|
|
16935
|
+
case "isotimestamp": return new Date().toISOString();
|
|
16936
|
+
case "isodate": return new Date().toISOString().slice(0, 10);
|
|
16937
|
+
case "randomint": {
|
|
16938
|
+
var lo = args.length > 1 ? args[0] : 0;
|
|
16939
|
+
var hi = args.length > 1 ? args[1] : args.length ? args[0] : 1000;
|
|
16940
|
+
if (!isFinite(lo) || !isFinite(hi)) return undefined;
|
|
16941
|
+
var min = Math.ceil(Math.min(lo, hi)), max = Math.floor(Math.max(lo, hi));
|
|
16942
|
+
return String(min + __atcRand(max - min + 1));
|
|
16943
|
+
}
|
|
16944
|
+
case "randomstring": {
|
|
16945
|
+
var len = args.length && isFinite(args[0]) ? Math.max(1, Math.min(1000, Math.floor(args[0]))) : 8;
|
|
16946
|
+
return __atcRandText(len, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
|
|
16947
|
+
}
|
|
16948
|
+
case "randomemail": return "user." + __atcRandText(8, "abcdefghijklmnopqrstuvwxyz0123456789") + "@example.com";
|
|
16949
|
+
case "randomfirstname": return __ATC_FIRST[__atcRand(__ATC_FIRST.length)];
|
|
16950
|
+
case "randomlastname": return __ATC_LAST[__atcRand(__ATC_LAST.length)];
|
|
16951
|
+
case "randomfullname": return __ATC_FIRST[__atcRand(__ATC_FIRST.length)] + " " + __ATC_LAST[__atcRand(__ATC_LAST.length)];
|
|
16952
|
+
case "randomboolean": return Math.random() < 0.5 ? "true" : "false";
|
|
16953
|
+
}
|
|
16954
|
+
return undefined;
|
|
16955
|
+
}
|
|
16956
|
+
function __atcFill(text, vars, mode, builtins) {
|
|
16683
16957
|
if (typeof text !== "string") return text;
|
|
16684
|
-
return text.replace(/\{\{\s*([A-Za-z_][\w.-]*)\s*\}\}|%7B%7B([A-Za-z_][\w.-]*)
|
|
16958
|
+
return text.replace(/\{\{\s*(\$?[A-Za-z_][\w.-]*)(?:\(([^(){}]*)\))?\s*\}\}|%7B%7B(\$?[A-Za-z_][\w.-]*)(?:\(([^(){}]*)\))?%7D%7D/gi, function (whole, a, aArg, b, bArg) {
|
|
16685
16959
|
var name = a || b;
|
|
16686
|
-
|
|
16687
|
-
var v
|
|
16960
|
+
var arg = a ? aArg : bArg;
|
|
16961
|
+
var v;
|
|
16962
|
+
if (name.charAt(0) === "$") {
|
|
16963
|
+
if (builtins === false) return whole;
|
|
16964
|
+
if (b && arg !== undefined) { try { arg = decodeURIComponent(arg); } catch (e) {} }
|
|
16965
|
+
v = __atcBuiltin(name, arg);
|
|
16966
|
+
if (v === undefined) return whole;
|
|
16967
|
+
} else {
|
|
16968
|
+
if (arg !== undefined || !Object.prototype.hasOwnProperty.call(vars, name)) return whole;
|
|
16969
|
+
v = String(vars[name]);
|
|
16970
|
+
}
|
|
16688
16971
|
if (mode === "url") return encodeURIComponent(v);
|
|
16689
16972
|
if (mode === "json") return JSON.stringify(v).slice(1, -1);
|
|
16690
16973
|
return v;
|
|
@@ -16696,6 +16979,32 @@ function __atcFill(text, vars, mode) {
|
|
|
16696
16979
|
}
|
|
16697
16980
|
});
|
|
16698
16981
|
|
|
16982
|
+
// ../shared/src/codegen.ts
|
|
16983
|
+
function parseSetCookie(header) {
|
|
16984
|
+
const [pair, ...attrs] = header.split(";");
|
|
16985
|
+
const eq = pair?.indexOf("=") ?? -1;
|
|
16986
|
+
if (!pair || eq <= 0) return null;
|
|
16987
|
+
const cookie = { name: pair.slice(0, eq).trim(), value: pair.slice(eq + 1).trim(), httpOnly: false, secure: false };
|
|
16988
|
+
for (const attr of attrs) {
|
|
16989
|
+
const i = attr.indexOf("=");
|
|
16990
|
+
const key = (i < 0 ? attr : attr.slice(0, i)).trim().toLowerCase();
|
|
16991
|
+
const value = i < 0 ? "" : attr.slice(i + 1).trim();
|
|
16992
|
+
if (key === "domain") cookie.domain = value;
|
|
16993
|
+
else if (key === "path") cookie.path = value;
|
|
16994
|
+
else if (key === "expires") cookie.expires = value;
|
|
16995
|
+
else if (key === "max-age" && /^-?\d+$/.test(value)) cookie.maxAge = Number(value);
|
|
16996
|
+
else if (key === "httponly") cookie.httpOnly = true;
|
|
16997
|
+
else if (key === "secure") cookie.secure = true;
|
|
16998
|
+
else if (key === "samesite") cookie.sameSite = value;
|
|
16999
|
+
}
|
|
17000
|
+
return cookie;
|
|
17001
|
+
}
|
|
17002
|
+
var init_codegen = __esm({
|
|
17003
|
+
"../shared/src/codegen.ts"() {
|
|
17004
|
+
"use strict";
|
|
17005
|
+
}
|
|
17006
|
+
});
|
|
17007
|
+
|
|
16699
17008
|
// ../../node_modules/.pnpm/yaml@2.9.1/node_modules/yaml/dist/nodes/identity.js
|
|
16700
17009
|
var require_identity = __commonJS({
|
|
16701
17010
|
"../../node_modules/.pnpm/yaml@2.9.1/node_modules/yaml/dist/nodes/identity.js"(exports) {
|
|
@@ -24098,6 +24407,10 @@ function defaultRequestDraft() {
|
|
|
24098
24407
|
params: [],
|
|
24099
24408
|
headers: [],
|
|
24100
24409
|
bodyMode: "none",
|
|
24410
|
+
graphqlQuery: "",
|
|
24411
|
+
graphqlVariables: "",
|
|
24412
|
+
graphqlOperation: "",
|
|
24413
|
+
graphqlErrorsFail: true,
|
|
24101
24414
|
bodyText: "",
|
|
24102
24415
|
authMode: "inherit",
|
|
24103
24416
|
oauth: defaultOAuth(),
|
|
@@ -24115,7 +24428,9 @@ function defaultRequestDraft() {
|
|
|
24115
24428
|
maxRedirects: 5,
|
|
24116
24429
|
connectionReuse: true,
|
|
24117
24430
|
useCookies: true,
|
|
24118
|
-
|
|
24431
|
+
// On for new requests, so test servers with their own certificates just work. Saved requests
|
|
24432
|
+
// keep what they have; one saved without the setting checks certificates (as before).
|
|
24433
|
+
tlsSkip: true,
|
|
24119
24434
|
checks: [],
|
|
24120
24435
|
before: null,
|
|
24121
24436
|
testData: null,
|
|
@@ -24157,7 +24472,7 @@ function requestFromUnknown(value) {
|
|
|
24157
24472
|
const base = defaultRequestDraft();
|
|
24158
24473
|
if (!value || typeof value !== "object") return base;
|
|
24159
24474
|
const item = value;
|
|
24160
|
-
const bodyMode = item.bodyMode === "json" || item.bodyMode === "text" ? item.bodyMode : "none";
|
|
24475
|
+
const bodyMode = item.bodyMode === "json" || item.bodyMode === "text" || item.bodyMode === "graphql" ? item.bodyMode : "none";
|
|
24161
24476
|
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";
|
|
24162
24477
|
return {
|
|
24163
24478
|
method: METHODS.includes(item.method) ? item.method : "GET",
|
|
@@ -24168,6 +24483,10 @@ function requestFromUnknown(value) {
|
|
|
24168
24483
|
headers: rows(item.headers),
|
|
24169
24484
|
bodyMode,
|
|
24170
24485
|
bodyText: str(item.bodyText, ""),
|
|
24486
|
+
graphqlQuery: str(item.graphqlQuery, ""),
|
|
24487
|
+
graphqlVariables: str(item.graphqlVariables, ""),
|
|
24488
|
+
graphqlOperation: str(item.graphqlOperation, ""),
|
|
24489
|
+
graphqlErrorsFail: item.graphqlErrorsFail !== false,
|
|
24171
24490
|
authMode: authMode2,
|
|
24172
24491
|
oauth: oauthFromUnknown(item.oauth),
|
|
24173
24492
|
oauth1: oauth1FromUnknown(item.oauth1),
|
|
@@ -24298,8 +24617,21 @@ function requestParts(draft) {
|
|
|
24298
24617
|
} catch (err) {
|
|
24299
24618
|
const bare = draftRuntimeNames(draft).find((n) => new RegExp(`(^|[^"])\\{\\{\\s*${n.replace(/[.-]/g, "\\$&")}\\s*\\}\\}(?!")`).test(draft.bodyText));
|
|
24300
24619
|
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`);
|
|
24620
|
+
const builtin = /(^|[^"])\{\{\s*(\$[A-Za-z]+)/.exec(draft.bodyText)?.[2];
|
|
24621
|
+
if (builtin) throw new Error(`Put quotes around {{${builtin}}} in the JSON body \u2014 built-in values are filled in as text`);
|
|
24301
24622
|
throw new Error(`Body is not valid JSON: ${err instanceof Error ? err.message : "parse error"}`);
|
|
24302
24623
|
}
|
|
24624
|
+
} else if (hasBody(draft.method) && draft.bodyMode === "graphql") {
|
|
24625
|
+
if (!draft.graphqlQuery.trim()) throw new Error("Write the GraphQL query");
|
|
24626
|
+
let variables = {};
|
|
24627
|
+
if (draft.graphqlVariables.trim()) {
|
|
24628
|
+
try {
|
|
24629
|
+
variables = JSON.parse(draft.graphqlVariables);
|
|
24630
|
+
} catch (err) {
|
|
24631
|
+
throw new Error(`GraphQL variables are not valid JSON: ${err instanceof Error ? err.message : "parse error"} \u2014 put quotes around "{{values}}" from test data`);
|
|
24632
|
+
}
|
|
24633
|
+
}
|
|
24634
|
+
body = { mode: "json", json: { query: draft.graphqlQuery, variables, ...draft.graphqlOperation.trim() ? { operationName: draft.graphqlOperation.trim() } : {} } };
|
|
24303
24635
|
} else if (hasBody(draft.method) && draft.bodyMode === "text") {
|
|
24304
24636
|
body = { mode: "text", text: draft.bodyText };
|
|
24305
24637
|
}
|
|
@@ -24311,7 +24643,11 @@ function requestParts(draft) {
|
|
|
24311
24643
|
headers: draft.headers.filter((h) => h.key.trim()),
|
|
24312
24644
|
body,
|
|
24313
24645
|
expectedStatus: [],
|
|
24314
|
-
|
|
24646
|
+
// GraphQL answers 200 even when the query failed; "errors" in the answer is the failure.
|
|
24647
|
+
checks: [
|
|
24648
|
+
...usableChecks(draft.checks),
|
|
24649
|
+
...hasBody(draft.method) && draft.bodyMode === "graphql" && draft.graphqlErrorsFail ? [{ type: "json", path: "errors", op: "not_exists", value: "" }] : []
|
|
24650
|
+
]
|
|
24315
24651
|
},
|
|
24316
24652
|
auth,
|
|
24317
24653
|
options: {
|
|
@@ -24324,12 +24660,21 @@ function requestParts(draft) {
|
|
|
24324
24660
|
}
|
|
24325
24661
|
};
|
|
24326
24662
|
}
|
|
24663
|
+
function isJsonObject(text) {
|
|
24664
|
+
try {
|
|
24665
|
+
const v = JSON.parse(text);
|
|
24666
|
+
return Boolean(v) && typeof v === "object" && !Array.isArray(v);
|
|
24667
|
+
} catch {
|
|
24668
|
+
return false;
|
|
24669
|
+
}
|
|
24670
|
+
}
|
|
24327
24671
|
function usableChecks(checks) {
|
|
24328
24672
|
return checks.filter((c) => {
|
|
24329
24673
|
if (c.type === "status") return c.expect.trim().length > 0;
|
|
24330
|
-
if (c.type === "body") return c.value.length > 0;
|
|
24674
|
+
if (c.type === "body") return c.op === "is_json" || c.value.length > 0;
|
|
24331
24675
|
if (c.type === "json") return c.path.trim().length > 0;
|
|
24332
24676
|
if (c.type === "header") return c.name.trim().length > 0;
|
|
24677
|
+
if (c.type === "schema") return isJsonObject(c.schema);
|
|
24333
24678
|
return c.maxMs > 0;
|
|
24334
24679
|
});
|
|
24335
24680
|
}
|
|
@@ -24462,6 +24807,8 @@ var init_draft = __esm({
|
|
|
24462
24807
|
"params",
|
|
24463
24808
|
"headers",
|
|
24464
24809
|
"bodyText",
|
|
24810
|
+
"graphqlQuery",
|
|
24811
|
+
"graphqlVariables",
|
|
24465
24812
|
"token",
|
|
24466
24813
|
"username",
|
|
24467
24814
|
"password",
|
|
@@ -24604,10 +24951,10 @@ function sustainedFrom(buckets) {
|
|
|
24604
24951
|
function percentileFromHist(hist, p) {
|
|
24605
24952
|
const total = hist.reduce((a, n) => a + n, 0);
|
|
24606
24953
|
if (!total) return null;
|
|
24607
|
-
let
|
|
24954
|
+
let seen2 = 0;
|
|
24608
24955
|
for (let i = 0; i < hist.length; i++) {
|
|
24609
|
-
|
|
24610
|
-
if (
|
|
24956
|
+
seen2 += hist[i];
|
|
24957
|
+
if (seen2 / total >= p) {
|
|
24611
24958
|
const edge = LATENCY_BIN_EDGES_MS[i];
|
|
24612
24959
|
return Number.isFinite(edge) ? edge : LATENCY_BIN_EDGES_MS[LATENCY_BIN_EDGES_MS.length - 2];
|
|
24613
24960
|
}
|
|
@@ -25137,7 +25484,7 @@ function stepOutcome(r) {
|
|
|
25137
25484
|
if (!failed.length) return { state: "passed" };
|
|
25138
25485
|
return {
|
|
25139
25486
|
state: "failed",
|
|
25140
|
-
message: `${failed.length} of ${r.checks.length} check${r.checks.length === 1 ? "" : "s"} failed: ${failed.map((c) => c.name).join("; ")}`
|
|
25487
|
+
message: `${failed.length} of ${r.checks.length} check${r.checks.length === 1 ? "" : "s"} failed: ${failed.map((c) => c.detail ? `${c.name} (${c.detail})` : c.name).join("; ")}`
|
|
25141
25488
|
};
|
|
25142
25489
|
}
|
|
25143
25490
|
if (r.status >= 200 && r.status < 400) return { state: "passed" };
|
|
@@ -25160,16 +25507,16 @@ function suitePassed(run2) {
|
|
|
25160
25507
|
}
|
|
25161
25508
|
function orderSuite(items, plan) {
|
|
25162
25509
|
const byId = new Map(items.map((i) => [i.id, i]));
|
|
25163
|
-
const
|
|
25510
|
+
const seen2 = /* @__PURE__ */ new Set();
|
|
25164
25511
|
const out = [];
|
|
25165
25512
|
for (const id of plan?.order ?? []) {
|
|
25166
25513
|
const item = byId.get(id);
|
|
25167
|
-
if (item && !
|
|
25514
|
+
if (item && !seen2.has(id)) {
|
|
25168
25515
|
out.push(item);
|
|
25169
|
-
|
|
25516
|
+
seen2.add(id);
|
|
25170
25517
|
}
|
|
25171
25518
|
}
|
|
25172
|
-
for (const item of items) if (!
|
|
25519
|
+
for (const item of items) if (!seen2.has(item.id)) out.push(item);
|
|
25173
25520
|
const skip = new Set(plan?.skip ?? []);
|
|
25174
25521
|
return out.map((item) => ({ item, skipped: skip.has(item.id) }));
|
|
25175
25522
|
}
|
|
@@ -25679,6 +26026,13 @@ function __atcOAuth1(method, url, headers, body) {
|
|
|
25679
26026
|
}
|
|
25680
26027
|
});
|
|
25681
26028
|
|
|
26029
|
+
// ../shared/src/graphql.ts
|
|
26030
|
+
var init_graphql = __esm({
|
|
26031
|
+
"../shared/src/graphql.ts"() {
|
|
26032
|
+
"use strict";
|
|
26033
|
+
}
|
|
26034
|
+
});
|
|
26035
|
+
|
|
25682
26036
|
// ../shared/src/export-fields.ts
|
|
25683
26037
|
function roundN(n, digits = 2) {
|
|
25684
26038
|
if (!Number.isFinite(n)) return 0;
|
|
@@ -26627,6 +26981,7 @@ var init_src = __esm({
|
|
|
26627
26981
|
init_dashboard();
|
|
26628
26982
|
init_workspace();
|
|
26629
26983
|
init_chain();
|
|
26984
|
+
init_codegen();
|
|
26630
26985
|
init_import();
|
|
26631
26986
|
init_email_report();
|
|
26632
26987
|
init_junit();
|
|
@@ -26636,6 +26991,7 @@ var init_src = __esm({
|
|
|
26636
26991
|
init_ci_templates();
|
|
26637
26992
|
init_sigv4();
|
|
26638
26993
|
init_oauth1();
|
|
26994
|
+
init_graphql();
|
|
26639
26995
|
init_latency_bins();
|
|
26640
26996
|
init_checks3();
|
|
26641
26997
|
init_definition();
|
|
@@ -26738,7 +27094,7 @@ token, password, or key.
|
|
|
26738
27094
|
async scan() {
|
|
26739
27095
|
await this.ensure();
|
|
26740
27096
|
let changed = false;
|
|
26741
|
-
const
|
|
27097
|
+
const seen2 = /* @__PURE__ */ new Set();
|
|
26742
27098
|
for (const folder of ["runs", "compares"]) {
|
|
26743
27099
|
let names = [];
|
|
26744
27100
|
try {
|
|
@@ -26748,7 +27104,7 @@ token, password, or key.
|
|
|
26748
27104
|
}
|
|
26749
27105
|
for (const name of names) {
|
|
26750
27106
|
const key = `${folder}/${name}`;
|
|
26751
|
-
|
|
27107
|
+
seen2.add(key);
|
|
26752
27108
|
let info;
|
|
26753
27109
|
try {
|
|
26754
27110
|
info = await stat(join(this.folder(folder), name));
|
|
@@ -26762,7 +27118,7 @@ token, password, or key.
|
|
|
26762
27118
|
}
|
|
26763
27119
|
}
|
|
26764
27120
|
for (const key of [...this.entries.keys()]) {
|
|
26765
|
-
if (!
|
|
27121
|
+
if (!seen2.has(key)) {
|
|
26766
27122
|
this.entries.delete(key);
|
|
26767
27123
|
changed = true;
|
|
26768
27124
|
}
|
|
@@ -27149,6 +27505,11 @@ function generateK6Script(run2, extra2 = {}) {
|
|
|
27149
27505
|
ATC_NAME: run2.name,
|
|
27150
27506
|
ATC_CHECKS: JSON.stringify(run2.request.checks.map((check2) => ({ label: checkLabel(check2), check: check2 })))
|
|
27151
27507
|
};
|
|
27508
|
+
if (run2.request.checks.some((c) => c.type === "schema")) {
|
|
27509
|
+
files["checks.json"] = env.ATC_CHECKS;
|
|
27510
|
+
env.ATC_CHECKS = "";
|
|
27511
|
+
env.ATC_CHECKS_FILE = "1";
|
|
27512
|
+
}
|
|
27152
27513
|
if (extra2.tlsAuth?.length) env.ATC_TLS_AUTH = JSON.stringify(extra2.tlsAuth);
|
|
27153
27514
|
if (extra2.jwt) {
|
|
27154
27515
|
const { tokens, ...rest } = extra2.jwt;
|
|
@@ -27179,15 +27540,14 @@ function generateK6Script(run2, extra2 = {}) {
|
|
|
27179
27540
|
env.ATC_AUTH_USER = run2.auth.mode === "ntlm" && run2.auth.domain ? `${run2.auth.domain}\\${run2.auth.username}` : run2.auth.username;
|
|
27180
27541
|
env.ATC_AUTH_PASS = run2.auth.password;
|
|
27181
27542
|
}
|
|
27543
|
+
const bodyMode = run2.request.body.mode;
|
|
27544
|
+
env.ATC_BODY_FILL = bodyMode === "json" ? "json" : bodyMode === "form" ? "url" : "raw";
|
|
27182
27545
|
if (data) {
|
|
27183
27546
|
files["data.json"] = JSON.stringify(data.rows);
|
|
27184
27547
|
env.ATC_DATA_COLUMNS = JSON.stringify(data.columns);
|
|
27185
27548
|
env.ATC_DATA_ORDER = data.order;
|
|
27186
|
-
const mode = run2.request.body.mode;
|
|
27187
|
-
env.ATC_BODY_FILL = mode === "json" ? "json" : mode === "form" ? "url" : "raw";
|
|
27188
27549
|
}
|
|
27189
27550
|
const dataBlock = data ? `
|
|
27190
|
-
${FILL_JS}
|
|
27191
27551
|
const ROWS = new SharedArray("rows", function () { return JSON.parse(open("./data.json")); });
|
|
27192
27552
|
const COLUMNS = JSON.parse(__ENV.ATC_DATA_COLUMNS);
|
|
27193
27553
|
function __atcRow() {
|
|
@@ -27211,12 +27571,12 @@ import { SharedArray } from "k6/data";
|
|
|
27211
27571
|
export const options = Object.assign(${optionsBlock(run2)}, __ENV.ATC_TLS_AUTH ? { tlsAuth: JSON.parse(__ENV.ATC_TLS_AUTH) } : {});
|
|
27212
27572
|
|
|
27213
27573
|
${CHECK_EVALUATOR_JS}
|
|
27214
|
-
${dataBlock}${run2.auth.mode === "aws_sigv4" ? `
|
|
27574
|
+
${FILL_JS}${dataBlock}${run2.auth.mode === "aws_sigv4" ? `
|
|
27215
27575
|
const AWS = JSON.parse(__ENV.ATC_AWS);
|
|
27216
27576
|
${SIGV4_JS}` : ""}${run2.auth.mode === "oauth1" ? `
|
|
27217
27577
|
const OAUTH1 = JSON.parse(__ENV.ATC_OAUTH1);
|
|
27218
27578
|
${OAUTH1_JS}` : ""}
|
|
27219
|
-
const CHECKS = JSON.parse(__ENV.ATC_CHECKS || "[]");
|
|
27579
|
+
const CHECKS = __ENV.ATC_CHECKS_FILE ? JSON.parse(open("./checks.json")) : JSON.parse(__ENV.ATC_CHECKS || "[]");
|
|
27220
27580
|
// JWT: tokens signed by the app for the whole run; each is used for its stretch of time.
|
|
27221
27581
|
const JWT = __ENV.ATC_JWT ? JSON.parse(__ENV.ATC_JWT) : null;
|
|
27222
27582
|
const JWTS = JWT ? JSON.parse(open("./jwt.json")) : [];
|
|
@@ -27244,7 +27604,9 @@ function __atcFreshToken() {
|
|
|
27244
27604
|
}
|
|
27245
27605
|
|
|
27246
27606
|
export default function () {
|
|
27247
|
-
|
|
27607
|
+
// Test-data values, and built-in values ({{$uuid}}, \u2026) made anew for every request.
|
|
27608
|
+
const vars = ${data ? "__atcRow()" : "{}"};
|
|
27609
|
+
const f = function (text, mode) { return __atcFill(text, vars, mode || "raw"); };
|
|
27248
27610
|
const headers = JSON.parse(__ENV.ATC_HEADERS || "{}");
|
|
27249
27611
|
for (const key in headers) headers[key] = f(headers[key]);
|
|
27250
27612
|
if (__ENV.ATC_COOKIE) headers.Cookie = __ENV.ATC_COOKIE;
|
|
@@ -45340,13 +45702,13 @@ function toPem(m) {
|
|
|
45340
45702
|
}
|
|
45341
45703
|
async function k6TlsAuth(urls) {
|
|
45342
45704
|
const out = [];
|
|
45343
|
-
const
|
|
45705
|
+
const seen2 = /* @__PURE__ */ new Set();
|
|
45344
45706
|
for (const target of urls) {
|
|
45345
45707
|
const m = await certificateFor(target);
|
|
45346
45708
|
if (!m) continue;
|
|
45347
45709
|
const domain2 = new URL(target).hostname;
|
|
45348
|
-
if (
|
|
45349
|
-
|
|
45710
|
+
if (seen2.has(domain2)) continue;
|
|
45711
|
+
seen2.add(domain2);
|
|
45350
45712
|
const pem = toPem(m);
|
|
45351
45713
|
out.push({ domains: [domain2], cert: pem.cert, key: pem.key, ...pem.password ? { password: pem.password } : {} });
|
|
45352
45714
|
}
|
|
@@ -45451,7 +45813,7 @@ function defaultPath(url2) {
|
|
|
45451
45813
|
if (!p.startsWith("/") || p.lastIndexOf("/") === 0) return "/";
|
|
45452
45814
|
return p.slice(0, p.lastIndexOf("/"));
|
|
45453
45815
|
}
|
|
45454
|
-
function
|
|
45816
|
+
function parseSetCookie2(header, url2, now = Date.now()) {
|
|
45455
45817
|
const [pair, ...attrs] = header.split(";");
|
|
45456
45818
|
const eq = pair.indexOf("=");
|
|
45457
45819
|
if (eq <= 0) return null;
|
|
@@ -45502,7 +45864,7 @@ async function storeCookies(target, setCookie) {
|
|
|
45502
45864
|
const now = Date.now();
|
|
45503
45865
|
let next = [...await load()];
|
|
45504
45866
|
for (const header of Array.isArray(setCookie) ? setCookie : [setCookie]) {
|
|
45505
|
-
const c =
|
|
45867
|
+
const c = parseSetCookie2(header, url2, now);
|
|
45506
45868
|
if (!c) continue;
|
|
45507
45869
|
next = next.filter((o) => !(o.name === c.name && o.domain === c.domain && o.path === c.path));
|
|
45508
45870
|
if (c.expires === null || c.expires > now) next.push(c);
|
|
@@ -69211,7 +69573,7 @@ var require_parse = __commonJS({
|
|
|
69211
69573
|
var { maxNameValuePairSize, maxAttributeValueSize } = require_constants4();
|
|
69212
69574
|
var { isCTLExcludingHtab } = require_util5();
|
|
69213
69575
|
var assert2 = __require("node:assert");
|
|
69214
|
-
function
|
|
69576
|
+
function parseSetCookie3(header) {
|
|
69215
69577
|
if (isCTLExcludingHtab(header)) {
|
|
69216
69578
|
return null;
|
|
69217
69579
|
}
|
|
@@ -69335,7 +69697,7 @@ var require_parse = __commonJS({
|
|
|
69335
69697
|
return parseUnparsedAttributes(unparsedAttributes, cookieAttributeList);
|
|
69336
69698
|
}
|
|
69337
69699
|
module.exports = {
|
|
69338
|
-
parseSetCookie:
|
|
69700
|
+
parseSetCookie: parseSetCookie3,
|
|
69339
69701
|
parseUnparsedAttributes
|
|
69340
69702
|
};
|
|
69341
69703
|
}
|
|
@@ -69345,7 +69707,7 @@ var require_parse = __commonJS({
|
|
|
69345
69707
|
var require_cookies = __commonJS({
|
|
69346
69708
|
"../../node_modules/.pnpm/undici@8.10.0/node_modules/undici/lib/web/cookies/index.js"(exports, module) {
|
|
69347
69709
|
"use strict";
|
|
69348
|
-
var { parseSetCookie:
|
|
69710
|
+
var { parseSetCookie: parseSetCookie3 } = require_parse();
|
|
69349
69711
|
var { stringify } = require_util5();
|
|
69350
69712
|
var { webidl } = require_webidl();
|
|
69351
69713
|
var { Headers } = require_headers();
|
|
@@ -69384,11 +69746,11 @@ var require_cookies = __commonJS({
|
|
|
69384
69746
|
if (!cookies) {
|
|
69385
69747
|
return [];
|
|
69386
69748
|
}
|
|
69387
|
-
return cookies.map((pair) =>
|
|
69749
|
+
return cookies.map((pair) => parseSetCookie3(pair));
|
|
69388
69750
|
}
|
|
69389
69751
|
function parseCookie(cookie) {
|
|
69390
69752
|
cookie = webidl.converters.DOMString(cookie);
|
|
69391
|
-
return
|
|
69753
|
+
return parseSetCookie3(cookie);
|
|
69392
69754
|
}
|
|
69393
69755
|
function setCookie(headers, cookie) {
|
|
69394
69756
|
webidl.argumentLengthCheck(arguments, 2, "setCookie");
|
|
@@ -73116,7 +73478,7 @@ function parseLab(input) {
|
|
|
73116
73478
|
return labRequestSchema.parse(input);
|
|
73117
73479
|
}
|
|
73118
73480
|
async function runSetupStep(setup) {
|
|
73119
|
-
const sent = await sendOnce(setup);
|
|
73481
|
+
const sent = await sendOnce(fillLab(setup, {}));
|
|
73120
73482
|
const vars = {};
|
|
73121
73483
|
const values = [];
|
|
73122
73484
|
let problem = sent.result.error ? `could not reach it: ${sent.result.error}` : void 0;
|
|
@@ -73162,6 +73524,17 @@ async function runProbe(input) {
|
|
|
73162
73524
|
const saved = parsed.save?.length && !sent.result.error ? takeValues(parsed.save, sent) : void 0;
|
|
73163
73525
|
return { ...sent.result, setup, dataRow: parsed.data ? { row: 1, of: parsed.data.rows.length } : void 0, saved };
|
|
73164
73526
|
}
|
|
73527
|
+
async function probeFull(input) {
|
|
73528
|
+
const parsed = parseLab(input);
|
|
73529
|
+
let vars = {};
|
|
73530
|
+
if (parsed.setup) {
|
|
73531
|
+
const step = await runSetupStep(parsed.setup);
|
|
73532
|
+
if (!step.result.ok) return { status: 0, body: "", error: step.result.error };
|
|
73533
|
+
vars = step.vars;
|
|
73534
|
+
}
|
|
73535
|
+
const sent = await sendOnce(fillLab(parsed, vars));
|
|
73536
|
+
return { status: sent.result.status, body: sent.raw.slice(0, 2e7), error: sent.result.error };
|
|
73537
|
+
}
|
|
73165
73538
|
function takeValues(rules, sent) {
|
|
73166
73539
|
const res = { status: sent.result.status, headers: sent.lowered, body: sent.raw, durationMs: sent.result.durationMs };
|
|
73167
73540
|
return rules.map((rule) => {
|
|
@@ -73174,6 +73547,12 @@ function takeValues(rules, sent) {
|
|
|
73174
73547
|
});
|
|
73175
73548
|
}
|
|
73176
73549
|
async function sendOnce(lab) {
|
|
73550
|
+
const out = await sendOnceAuthed(lab);
|
|
73551
|
+
const note = AUTH_NOTES[lab.auth.mode];
|
|
73552
|
+
if (note && out.result.sent) out.result.sent.authNote = note;
|
|
73553
|
+
return out;
|
|
73554
|
+
}
|
|
73555
|
+
async function sendOnceAuthed(lab) {
|
|
73177
73556
|
if (lab.auth.mode === "digest" || lab.auth.mode === "ntlm") return sendChallenged(lab, lab.auth);
|
|
73178
73557
|
if (lab.auth.mode === "jwt") {
|
|
73179
73558
|
const { mode: _mode, ...config3 } = lab.auth;
|
|
@@ -73251,6 +73630,7 @@ async function sendRaw(lab, extra2 = {}) {
|
|
|
73251
73630
|
headers[pair.key] = pair.value;
|
|
73252
73631
|
}
|
|
73253
73632
|
const secretNames = applyAuth(headers, url2, lab.auth);
|
|
73633
|
+
const secretQuery = lab.auth.mode === "api_key" && lab.auth.in === "query" ? [lab.auth.name] : [];
|
|
73254
73634
|
if (extra2.authorization) {
|
|
73255
73635
|
headers.authorization = extra2.authorization;
|
|
73256
73636
|
secretNames.push("authorization");
|
|
@@ -73267,7 +73647,10 @@ async function sendRaw(lab, extra2 = {}) {
|
|
|
73267
73647
|
if (signed.authorization) {
|
|
73268
73648
|
headers.authorization = signed.authorization;
|
|
73269
73649
|
secretNames.push("authorization");
|
|
73270
|
-
} else
|
|
73650
|
+
} else {
|
|
73651
|
+
target = new URL(signed.url);
|
|
73652
|
+
secretQuery.push("oauth_signature", "oauth_token");
|
|
73653
|
+
}
|
|
73271
73654
|
}
|
|
73272
73655
|
if (lab.auth.mode === "aws_sigv4") {
|
|
73273
73656
|
const { mode: _mode, ...creds } = lab.auth;
|
|
@@ -73281,6 +73664,8 @@ async function sendRaw(lab, extra2 = {}) {
|
|
|
73281
73664
|
const agent = extra2.dispatcher ?? new import_undici2.Agent({ connect: await tlsConnect(url2, options.tlsSkipVerify) });
|
|
73282
73665
|
const dispatcher = extra2.dispatcher ?? (useJar ? agent : agent.compose(import_undici2.interceptors.redirect({ maxRedirections: options.maxRedirects })));
|
|
73283
73666
|
const started = performance.now();
|
|
73667
|
+
let sent;
|
|
73668
|
+
const cookies = [];
|
|
73284
73669
|
try {
|
|
73285
73670
|
let current = target;
|
|
73286
73671
|
let method = lab.request.method;
|
|
@@ -73292,9 +73677,16 @@ async function sendRaw(lab, extra2 = {}) {
|
|
|
73292
73677
|
const jar2 = await cookieHeader(current);
|
|
73293
73678
|
if (jar2) sendHeaders.cookie = jar2;
|
|
73294
73679
|
}
|
|
73680
|
+
if (!sent) sent = describeSent(method, current, sendHeaders, body, [...secretNames, ...ALWAYS_SECRET], secretQuery, options.tlsSkipVerify);
|
|
73681
|
+
else (sent.redirects ??= []).push(`${method} ${current.toString()}`);
|
|
73295
73682
|
response = await (0, import_undici2.request)(current, { method, headers: sendHeaders, body, headersTimeout: timeout, bodyTimeout: timeout, dispatcher });
|
|
73683
|
+
const setCookie = response.headers["set-cookie"];
|
|
73684
|
+
for (const line of typeof setCookie === "string" ? [setCookie] : setCookie ?? []) {
|
|
73685
|
+
const parsed = parseSetCookie(line);
|
|
73686
|
+
if (parsed) cookies.push(parsed);
|
|
73687
|
+
}
|
|
73296
73688
|
if (!useJar) break;
|
|
73297
|
-
await storeCookies(current,
|
|
73689
|
+
await storeCookies(current, setCookie);
|
|
73298
73690
|
const location = response.headers.location;
|
|
73299
73691
|
if (![301, 302, 303, 307, 308].includes(response.statusCode) || typeof location !== "string" || hops >= options.maxRedirects) break;
|
|
73300
73692
|
await response.body.dump();
|
|
@@ -73309,8 +73701,11 @@ async function sendRaw(lab, extra2 = {}) {
|
|
|
73309
73701
|
}
|
|
73310
73702
|
current = next;
|
|
73311
73703
|
}
|
|
73312
|
-
const
|
|
73704
|
+
const bytes2 = Buffer.from(await response.body.arrayBuffer());
|
|
73705
|
+
const raw = bytes2.toString("utf8");
|
|
73313
73706
|
const durationMs = Math.round((performance.now() - started) * 10) / 10;
|
|
73707
|
+
const responseType = String(response.headers["content-type"] ?? "").toLowerCase();
|
|
73708
|
+
const bodyBase64 = /^image\/(png|jpe?g|gif|webp|bmp|svg\+xml|x-icon|vnd\.microsoft\.icon|avif)/.test(responseType) && bytes2.length <= IMAGE_LIMIT ? bytes2.toString("base64") : void 0;
|
|
73314
73709
|
const truncated = raw.length > BODY_LIMIT;
|
|
73315
73710
|
const headerBag = {};
|
|
73316
73711
|
for (const [key, value] of Object.entries(response.headers)) {
|
|
@@ -73331,7 +73726,10 @@ async function sendRaw(lab, extra2 = {}) {
|
|
|
73331
73726
|
truncated,
|
|
73332
73727
|
durationMs,
|
|
73333
73728
|
url: url2.toString(),
|
|
73334
|
-
checks
|
|
73729
|
+
checks,
|
|
73730
|
+
sent,
|
|
73731
|
+
...cookies.length ? { cookies } : {},
|
|
73732
|
+
...bodyBase64 ? { bodyBase64 } : {}
|
|
73335
73733
|
}
|
|
73336
73734
|
};
|
|
73337
73735
|
} catch (err) {
|
|
@@ -73348,14 +73746,29 @@ async function sendRaw(lab, extra2 = {}) {
|
|
|
73348
73746
|
truncated: false,
|
|
73349
73747
|
durationMs,
|
|
73350
73748
|
url: url2.toString(),
|
|
73351
|
-
error: err instanceof Error ? err.message : String(err)
|
|
73749
|
+
error: err instanceof Error ? err.message : String(err),
|
|
73750
|
+
sent
|
|
73352
73751
|
}
|
|
73353
73752
|
};
|
|
73354
73753
|
} finally {
|
|
73355
73754
|
if (!extra2.dispatcher) await dispatcher.close();
|
|
73356
73755
|
}
|
|
73357
73756
|
}
|
|
73358
|
-
|
|
73757
|
+
function describeSent(method, url2, headers, body, secretHeaders, secretQuery, insecure) {
|
|
73758
|
+
const list = [["host", url2.host], ...Object.entries(headers)];
|
|
73759
|
+
if (body !== void 0) list.push(["content-length", String(Buffer.byteLength(body))]);
|
|
73760
|
+
return {
|
|
73761
|
+
method,
|
|
73762
|
+
url: url2.toString(),
|
|
73763
|
+
headers: list,
|
|
73764
|
+
...body !== void 0 ? { body: body.length > BODY_LIMIT ? body.slice(0, BODY_LIMIT) : body, bodyTruncated: body.length > BODY_LIMIT || void 0 } : {},
|
|
73765
|
+
secretHeaders: [...new Set(secretHeaders.map((h) => h.toLowerCase()))],
|
|
73766
|
+
secretQuery,
|
|
73767
|
+
// Only https has a certificate to skip.
|
|
73768
|
+
insecure: insecure && url2.protocol === "https:" || void 0
|
|
73769
|
+
};
|
|
73770
|
+
}
|
|
73771
|
+
var import_undici2, BODY_LIMIT, IMAGE_LIMIT, ALWAYS_SECRET, AUTH_NOTES;
|
|
73359
73772
|
var init_probe = __esm({
|
|
73360
73773
|
"../../apps/engine/src/probe.ts"() {
|
|
73361
73774
|
"use strict";
|
|
@@ -73369,6 +73782,16 @@ var init_probe = __esm({
|
|
|
73369
73782
|
init_cookies();
|
|
73370
73783
|
init_oauth();
|
|
73371
73784
|
BODY_LIMIT = 64 * 1024;
|
|
73785
|
+
IMAGE_LIMIT = 5 * 1024 * 1024;
|
|
73786
|
+
ALWAYS_SECRET = ["authorization", "proxy-authorization", "cookie", "x-api-key", "api-key", "x-amz-security-token"];
|
|
73787
|
+
AUTH_NOTES = {
|
|
73788
|
+
oauth2: "OAuth 2.0 \u2014 the app got the token and sent it as a Bearer token",
|
|
73789
|
+
oauth1: "OAuth 1.0 \u2014 signed for this one request (nonce and timestamp)",
|
|
73790
|
+
aws_sigv4: "AWS Signature \u2014 signed for this one request",
|
|
73791
|
+
jwt: "JWT \u2014 signed by the app for this request",
|
|
73792
|
+
digest: "Digest \u2014 answers the server's 401 challenge; this is the second request",
|
|
73793
|
+
ntlm: "NTLM \u2014 signs in over one connection; this is the last of three requests"
|
|
73794
|
+
};
|
|
73372
73795
|
}
|
|
73373
73796
|
});
|
|
73374
73797
|
|
|
@@ -73409,10 +73832,10 @@ var init_latency_histogram = __esm({
|
|
|
73409
73832
|
percentile(p) {
|
|
73410
73833
|
if (this.count === 0) return 0;
|
|
73411
73834
|
const rank = Math.ceil(p / 100 * this.count);
|
|
73412
|
-
let
|
|
73835
|
+
let seen2 = 0;
|
|
73413
73836
|
for (const bin of [...this.bins.keys()].sort((a, b) => a - b)) {
|
|
73414
|
-
|
|
73415
|
-
if (
|
|
73837
|
+
seen2 += this.bins.get(bin);
|
|
73838
|
+
if (seen2 >= rank) {
|
|
73416
73839
|
const mid = FLOOR_MS * Math.exp((bin + 0.5) * GROWTH);
|
|
73417
73840
|
return Math.min(this.max, Math.max(this.min, mid));
|
|
73418
73841
|
}
|
|
@@ -73923,7 +74346,7 @@ async function startRun(lab, repoRoot2) {
|
|
|
73923
74346
|
err.statusCode = 422;
|
|
73924
74347
|
throw err;
|
|
73925
74348
|
}
|
|
73926
|
-
filled = fillLab(lab, step.vars);
|
|
74349
|
+
filled = fillLab(lab, step.vars, { builtins: false });
|
|
73927
74350
|
}
|
|
73928
74351
|
const id = crypto.randomUUID();
|
|
73929
74352
|
const dir2 = await mkdtemp(join5(tmpdir2(), "atc-run-"));
|
|
@@ -74273,7 +74696,7 @@ function explainMissing(message, index, savers, steps) {
|
|
|
74273
74696
|
});
|
|
74274
74697
|
return hints.length ? hints.join("; ") : message;
|
|
74275
74698
|
}
|
|
74276
|
-
async function runStep(step, workspace, vars, envName, setups,
|
|
74699
|
+
async function runStep(step, workspace, vars, envName, setups, explain2 = (m) => m) {
|
|
74277
74700
|
const saved = await workspace.getRequest(step.id);
|
|
74278
74701
|
if (!saved) throw new StepProblem("This request is no longer saved");
|
|
74279
74702
|
if (saved.secretsMissing?.length) {
|
|
@@ -74283,7 +74706,7 @@ async function runStep(step, workspace, vars, envName, setups, explain = (m) =>
|
|
|
74283
74706
|
try {
|
|
74284
74707
|
draft = await resolveSaved(workspace, saved, vars, envName);
|
|
74285
74708
|
} catch (err) {
|
|
74286
|
-
throw new StepProblem(
|
|
74709
|
+
throw new StepProblem(explain2(err instanceof Error ? err.message.replace(/ — pick one next to the URL$/, " \u2014 pick an environment for this run") : String(err)));
|
|
74287
74710
|
}
|
|
74288
74711
|
const payload = buildPayload(draft, toLoadConfig(DEFAULT_LOAD), saved.name);
|
|
74289
74712
|
let runtime = {};
|
|
@@ -74309,23 +74732,23 @@ async function runStep(step, workspace, vars, envName, setups, explain = (m) =>
|
|
|
74309
74732
|
runtime = done.vars;
|
|
74310
74733
|
}
|
|
74311
74734
|
if (payload.data) runtime = { ...runtime, ...firstRowVars(payload.data) };
|
|
74312
|
-
const
|
|
74313
|
-
const outcome = stepOutcome(
|
|
74735
|
+
const probe2 = await runProbe({ ...fillLab({ request: payload.request, auth: payload.auth, options: payload.options }, runtime), save: payload.save });
|
|
74736
|
+
const outcome = stepOutcome(probe2);
|
|
74314
74737
|
step.state = outcome.state;
|
|
74315
74738
|
step.message = outcome.message;
|
|
74316
|
-
if (
|
|
74317
|
-
step.saved =
|
|
74318
|
-
for (const v of
|
|
74319
|
-
const missing =
|
|
74739
|
+
if (probe2.saved?.length) {
|
|
74740
|
+
step.saved = probe2.saved.map((v) => ({ name: v.name, found: v.found, preview: v.preview }));
|
|
74741
|
+
for (const v of probe2.saved) if (v.found && v.value !== void 0) vars[v.name] = v.value;
|
|
74742
|
+
const missing = probe2.saved.filter((v) => !v.found);
|
|
74320
74743
|
if (missing.length && step.state === "passed") {
|
|
74321
74744
|
step.state = "failed";
|
|
74322
74745
|
step.message = missing.map((v) => `{{${v.name}}} was not found in the answer (looked for ${v.from})`).join("; ");
|
|
74323
74746
|
}
|
|
74324
74747
|
}
|
|
74325
|
-
step.status =
|
|
74326
|
-
step.durationMs =
|
|
74327
|
-
step.checks =
|
|
74328
|
-
if (outcome.state !== "passed" &&
|
|
74748
|
+
step.status = probe2.status || void 0;
|
|
74749
|
+
step.durationMs = probe2.durationMs;
|
|
74750
|
+
step.checks = probe2.checks;
|
|
74751
|
+
if (outcome.state !== "passed" && probe2.body) step.bodyPreview = probe2.body.slice(0, BODY_PREVIEW_CHARS);
|
|
74329
74752
|
}
|
|
74330
74753
|
function registerSuiteRoutes(api, workspace, plans, builtins) {
|
|
74331
74754
|
api.get("/suites/plan", async (request3) => {
|
|
@@ -81412,32 +81835,32 @@ var require_err_helpers = __commonJS({
|
|
|
81412
81835
|
return isErrorLike(cause) ? cause : void 0;
|
|
81413
81836
|
}
|
|
81414
81837
|
};
|
|
81415
|
-
var _stackWithCauses = (err,
|
|
81838
|
+
var _stackWithCauses = (err, seen2) => {
|
|
81416
81839
|
if (!isErrorLike(err)) return "";
|
|
81417
81840
|
const stack = err.stack || "";
|
|
81418
|
-
if (
|
|
81841
|
+
if (seen2.has(err)) {
|
|
81419
81842
|
return stack + "\ncauses have become circular...";
|
|
81420
81843
|
}
|
|
81421
81844
|
const cause = getErrorCause(err);
|
|
81422
81845
|
if (cause) {
|
|
81423
|
-
|
|
81424
|
-
return stack + "\ncaused by: " + _stackWithCauses(cause,
|
|
81846
|
+
seen2.add(err);
|
|
81847
|
+
return stack + "\ncaused by: " + _stackWithCauses(cause, seen2);
|
|
81425
81848
|
} else {
|
|
81426
81849
|
return stack;
|
|
81427
81850
|
}
|
|
81428
81851
|
};
|
|
81429
81852
|
var stackWithCauses = (err) => _stackWithCauses(err, /* @__PURE__ */ new Set());
|
|
81430
|
-
var _messageWithCauses = (err,
|
|
81853
|
+
var _messageWithCauses = (err, seen2, skip) => {
|
|
81431
81854
|
if (!isErrorLike(err)) return "";
|
|
81432
81855
|
const message = skip ? "" : err.message || "";
|
|
81433
|
-
if (
|
|
81856
|
+
if (seen2.has(err)) {
|
|
81434
81857
|
return message + ": ...";
|
|
81435
81858
|
}
|
|
81436
81859
|
const cause = getErrorCause(err);
|
|
81437
81860
|
if (cause) {
|
|
81438
|
-
|
|
81861
|
+
seen2.add(err);
|
|
81439
81862
|
const skipIfVErrorStyleCause = typeof err.cause === "function";
|
|
81440
|
-
return message + (skipIfVErrorStyleCause ? "" : ": ") + _messageWithCauses(cause,
|
|
81863
|
+
return message + (skipIfVErrorStyleCause ? "" : ": ") + _messageWithCauses(cause, seen2, skipIfVErrorStyleCause);
|
|
81441
81864
|
} else {
|
|
81442
81865
|
return message;
|
|
81443
81866
|
}
|
|
@@ -81456,7 +81879,7 @@ var require_err_helpers = __commonJS({
|
|
|
81456
81879
|
var require_err_proto = __commonJS({
|
|
81457
81880
|
"../../node_modules/.pnpm/pino-std-serializers@7.1.0/node_modules/pino-std-serializers/lib/err-proto.js"(exports, module) {
|
|
81458
81881
|
"use strict";
|
|
81459
|
-
var
|
|
81882
|
+
var seen2 = /* @__PURE__ */ Symbol("circular-ref-tag");
|
|
81460
81883
|
var rawSymbol = /* @__PURE__ */ Symbol("pino-raw-err-ref");
|
|
81461
81884
|
var pinoErrProto = Object.create({}, {
|
|
81462
81885
|
type: {
|
|
@@ -81496,7 +81919,7 @@ var require_err_proto = __commonJS({
|
|
|
81496
81919
|
module.exports = {
|
|
81497
81920
|
pinoErrProto,
|
|
81498
81921
|
pinoErrorSymbols: {
|
|
81499
|
-
seen,
|
|
81922
|
+
seen: seen2,
|
|
81500
81923
|
rawSymbol
|
|
81501
81924
|
}
|
|
81502
81925
|
};
|
|
@@ -81510,13 +81933,13 @@ var require_err = __commonJS({
|
|
|
81510
81933
|
module.exports = errSerializer;
|
|
81511
81934
|
var { messageWithCauses, stackWithCauses, isErrorLike } = require_err_helpers();
|
|
81512
81935
|
var { pinoErrProto, pinoErrorSymbols } = require_err_proto();
|
|
81513
|
-
var { seen } = pinoErrorSymbols;
|
|
81936
|
+
var { seen: seen2 } = pinoErrorSymbols;
|
|
81514
81937
|
var { toString } = Object.prototype;
|
|
81515
81938
|
function errSerializer(err) {
|
|
81516
81939
|
if (!isErrorLike(err)) {
|
|
81517
81940
|
return err;
|
|
81518
81941
|
}
|
|
81519
|
-
err[
|
|
81942
|
+
err[seen2] = void 0;
|
|
81520
81943
|
const _err = Object.create(pinoErrProto);
|
|
81521
81944
|
_err.type = toString.call(err.constructor) === "[object Function]" ? err.constructor.name : err.name;
|
|
81522
81945
|
_err.message = messageWithCauses(err);
|
|
@@ -81528,7 +81951,7 @@ var require_err = __commonJS({
|
|
|
81528
81951
|
if (_err[key] === void 0) {
|
|
81529
81952
|
const val = err[key];
|
|
81530
81953
|
if (isErrorLike(val)) {
|
|
81531
|
-
if (key !== "cause" && !Object.prototype.hasOwnProperty.call(val,
|
|
81954
|
+
if (key !== "cause" && !Object.prototype.hasOwnProperty.call(val, seen2)) {
|
|
81532
81955
|
_err[key] = errSerializer(val);
|
|
81533
81956
|
}
|
|
81534
81957
|
} else {
|
|
@@ -81536,7 +81959,7 @@ var require_err = __commonJS({
|
|
|
81536
81959
|
}
|
|
81537
81960
|
}
|
|
81538
81961
|
}
|
|
81539
|
-
delete err[
|
|
81962
|
+
delete err[seen2];
|
|
81540
81963
|
_err.raw = err;
|
|
81541
81964
|
return _err;
|
|
81542
81965
|
}
|
|
@@ -81550,13 +81973,13 @@ var require_err_with_cause = __commonJS({
|
|
|
81550
81973
|
module.exports = errWithCauseSerializer;
|
|
81551
81974
|
var { isErrorLike } = require_err_helpers();
|
|
81552
81975
|
var { pinoErrProto, pinoErrorSymbols } = require_err_proto();
|
|
81553
|
-
var { seen } = pinoErrorSymbols;
|
|
81976
|
+
var { seen: seen2 } = pinoErrorSymbols;
|
|
81554
81977
|
var { toString } = Object.prototype;
|
|
81555
81978
|
function errWithCauseSerializer(err) {
|
|
81556
81979
|
if (!isErrorLike(err)) {
|
|
81557
81980
|
return err;
|
|
81558
81981
|
}
|
|
81559
|
-
err[
|
|
81982
|
+
err[seen2] = void 0;
|
|
81560
81983
|
const _err = Object.create(pinoErrProto);
|
|
81561
81984
|
_err.type = toString.call(err.constructor) === "[object Function]" ? err.constructor.name : err.name;
|
|
81562
81985
|
_err.message = err.message;
|
|
@@ -81564,14 +81987,14 @@ var require_err_with_cause = __commonJS({
|
|
|
81564
81987
|
if (Array.isArray(err.errors)) {
|
|
81565
81988
|
_err.aggregateErrors = err.errors.map((err2) => errWithCauseSerializer(err2));
|
|
81566
81989
|
}
|
|
81567
|
-
if (isErrorLike(err.cause) && !Object.prototype.hasOwnProperty.call(err.cause,
|
|
81990
|
+
if (isErrorLike(err.cause) && !Object.prototype.hasOwnProperty.call(err.cause, seen2)) {
|
|
81568
81991
|
_err.cause = errWithCauseSerializer(err.cause);
|
|
81569
81992
|
}
|
|
81570
81993
|
for (const key in err) {
|
|
81571
81994
|
if (_err[key] === void 0) {
|
|
81572
81995
|
const val = err[key];
|
|
81573
81996
|
if (isErrorLike(val)) {
|
|
81574
|
-
if (!Object.prototype.hasOwnProperty.call(val,
|
|
81997
|
+
if (!Object.prototype.hasOwnProperty.call(val, seen2)) {
|
|
81575
81998
|
_err[key] = errWithCauseSerializer(val);
|
|
81576
81999
|
}
|
|
81577
82000
|
} else {
|
|
@@ -81579,7 +82002,7 @@ var require_err_with_cause = __commonJS({
|
|
|
81579
82002
|
}
|
|
81580
82003
|
}
|
|
81581
82004
|
}
|
|
81582
|
-
delete err[
|
|
82005
|
+
delete err[seen2];
|
|
81583
82006
|
_err.raw = err;
|
|
81584
82007
|
return _err;
|
|
81585
82008
|
}
|
|
@@ -108734,7 +109157,7 @@ var require_dist5 = __commonJS({
|
|
|
108734
109157
|
exports.stringifyCookie = stringifyCookie;
|
|
108735
109158
|
exports.stringifySetCookie = stringifySetCookie;
|
|
108736
109159
|
exports.serialize = stringifySetCookie;
|
|
108737
|
-
exports.parseSetCookie =
|
|
109160
|
+
exports.parseSetCookie = parseSetCookie3;
|
|
108738
109161
|
exports.stringifySetCookie = stringifySetCookie;
|
|
108739
109162
|
exports.serialize = stringifySetCookie;
|
|
108740
109163
|
var cookieNameRegExp = /^[\u0021-\u003A\u003C\u003E-\u007E]+$/;
|
|
@@ -108871,7 +109294,7 @@ var require_dist5 = __commonJS({
|
|
|
108871
109294
|
}
|
|
108872
109295
|
return str2;
|
|
108873
109296
|
}
|
|
108874
|
-
function
|
|
109297
|
+
function parseSetCookie3(str2, options) {
|
|
108875
109298
|
const dec = options?.decode || decode4;
|
|
108876
109299
|
const len = str2.length;
|
|
108877
109300
|
const endIdx = endIndex(str2, 0, len);
|
|
@@ -111984,7 +112407,7 @@ import http from "node:http";
|
|
|
111984
112407
|
import https from "node:https";
|
|
111985
112408
|
function run(cmd, args) {
|
|
111986
112409
|
return new Promise((resolve5) => {
|
|
111987
|
-
execFile2(cmd, args, { timeout:
|
|
112410
|
+
execFile2(cmd, args, { timeout: 2e4, windowsHide: true, maxBuffer: 8 * 1024 * 1024 }, (err, stdout) => resolve5(err && !stdout ? "" : String(stdout)));
|
|
111988
112411
|
});
|
|
111989
112412
|
}
|
|
111990
112413
|
function local(address) {
|
|
@@ -112053,11 +112476,10 @@ function parseSs(text) {
|
|
|
112053
112476
|
async function listPorts() {
|
|
112054
112477
|
if (process.platform === "win32") {
|
|
112055
112478
|
const tasks = run("tasklist", ["/FO", "CSV", "/NH"]);
|
|
112056
|
-
const
|
|
112057
|
-
const found2 = parseNetstat(v4);
|
|
112058
|
-
for (const [port2, l] of parseNetstat(v6)) if (!found2.has(port2)) found2.set(port2, l);
|
|
112479
|
+
const found2 = parseNetstat(await run("netstat", ["-ano"]));
|
|
112059
112480
|
const named = tasks.then((text) => {
|
|
112060
112481
|
const names = parseTasklist(text);
|
|
112482
|
+
if (!names.size) return;
|
|
112061
112483
|
for (const l of found2.values()) {
|
|
112062
112484
|
const t = names.get(l.pid);
|
|
112063
112485
|
l.name = t?.name;
|
|
@@ -112081,20 +112503,34 @@ function ask(scheme, host2, port2, verify, timeoutMs) {
|
|
|
112081
112503
|
resolve5("yes");
|
|
112082
112504
|
}
|
|
112083
112505
|
);
|
|
112084
|
-
req.on("timeout", () =>
|
|
112506
|
+
req.on("timeout", () => {
|
|
112507
|
+
resolve5("slow");
|
|
112508
|
+
req.destroy(new Error("timeout"));
|
|
112509
|
+
});
|
|
112085
112510
|
req.on("error", (err) => resolve5(scheme === "https" && verify && /CERT|SELF_SIGNED|UNABLE_TO|SIGNATURE|HOSTNAME|ALTNAME/i.test(err.code ?? err.message) ? "cert" : "no"));
|
|
112086
112511
|
req.end();
|
|
112087
112512
|
});
|
|
112088
112513
|
}
|
|
112089
112514
|
async function identify(l, timeoutMs) {
|
|
112515
|
+
const before = seen.get(l.port);
|
|
112516
|
+
const server = await probe(l, timeoutMs);
|
|
112517
|
+
if (server && server !== "slow") {
|
|
112518
|
+
seen.set(l.port, { pid: l.pid, server });
|
|
112519
|
+
return server;
|
|
112520
|
+
}
|
|
112521
|
+
if (server === "slow" && before && before.pid === l.pid) return before.server;
|
|
112522
|
+
seen.delete(l.port);
|
|
112523
|
+
return null;
|
|
112524
|
+
}
|
|
112525
|
+
async function probe(l, timeoutMs) {
|
|
112090
112526
|
const host2 = l.ipv4 ? "127.0.0.1" : "::1";
|
|
112091
112527
|
const [plain, tls] = await Promise.all([ask("http", host2, l.port, true, timeoutMs), ask("https", host2, l.port, true, timeoutMs)]);
|
|
112092
112528
|
if (plain === "yes") return { port: l.port, scheme: "http", name: l.name };
|
|
112093
112529
|
if (tls === "yes") return { port: l.port, scheme: "https", name: l.name };
|
|
112094
112530
|
if (tls === "cert" && await ask("https", host2, l.port, false, timeoutMs) === "yes") return { port: l.port, scheme: "https", name: l.name, selfSigned: true };
|
|
112095
|
-
return null;
|
|
112531
|
+
return plain === "slow" || tls === "slow" ? "slow" : null;
|
|
112096
112532
|
}
|
|
112097
|
-
async function findLocalServers(skip = [], timeoutMs =
|
|
112533
|
+
async function findLocalServers(skip = [], timeoutMs = 1500) {
|
|
112098
112534
|
const { ports, named } = await listPorts();
|
|
112099
112535
|
const [answers] = await Promise.all([
|
|
112100
112536
|
Promise.all(ports.filter((l) => !skip.includes(l.port) && !NOT_HTTP.has(l.port)).map(async (l) => ({ l, server: await identify(l, timeoutMs) }))),
|
|
@@ -112102,12 +112538,13 @@ async function findLocalServers(skip = [], timeoutMs = 600) {
|
|
|
112102
112538
|
]);
|
|
112103
112539
|
return answers.filter(({ l, server }) => server && !l.service && !SYSTEM.has((l.name ?? "").toLowerCase())).map(({ l, server }) => ({ ...server, name: l.name })).sort((a, b) => a.port - b.port);
|
|
112104
112540
|
}
|
|
112105
|
-
var SYSTEM, NOT_HTTP;
|
|
112541
|
+
var SYSTEM, NOT_HTTP, seen;
|
|
112106
112542
|
var init_local_servers = __esm({
|
|
112107
112543
|
"../../apps/engine/src/local-servers.ts"() {
|
|
112108
112544
|
"use strict";
|
|
112109
112545
|
SYSTEM = /* @__PURE__ */ new Set(["system", "svchost", "lsass", "wininit", "services", "spoolsv", "launchd", "systemd", "systemd-resolved", "cupsd", "sshd", "rpcbind"]);
|
|
112110
112546
|
NOT_HTTP = /* @__PURE__ */ new Set([22, 25, 53, 135, 139, 445, 1433, 1521, 3306, 3389, 5432, 5672, 6379, 9042, 11211, 27017]);
|
|
112547
|
+
seen = /* @__PURE__ */ new Map();
|
|
112111
112548
|
}
|
|
112112
112549
|
});
|
|
112113
112550
|
|
|
@@ -112123,6 +112560,8 @@ async function routes(api) {
|
|
|
112123
112560
|
status: "ok",
|
|
112124
112561
|
service: "engine",
|
|
112125
112562
|
version: appVersion,
|
|
112563
|
+
// "cli": started by `atc ui` — the screens say so when it stops.
|
|
112564
|
+
host: process.env.ATC_HOST ?? (uiDir ? "desktop" : "dev"),
|
|
112126
112565
|
persistence: "files",
|
|
112127
112566
|
dataDir: history.dir,
|
|
112128
112567
|
k6: await detectK6(root),
|
|
@@ -112149,6 +112588,13 @@ async function routes(api) {
|
|
|
112149
112588
|
return usage.setEnabled(request3.body.enabled);
|
|
112150
112589
|
});
|
|
112151
112590
|
await api.register(async (demo) => registerDemoApi(demo), { prefix: "/demo-api", logLevel: "warn" });
|
|
112591
|
+
api.post("/graphql/introspect", async (request3, reply) => {
|
|
112592
|
+
try {
|
|
112593
|
+
return await probeFull(request3.body);
|
|
112594
|
+
} catch (err) {
|
|
112595
|
+
return reply.code(400).send({ error: err instanceof Error ? err.message : "invalid request" });
|
|
112596
|
+
}
|
|
112597
|
+
});
|
|
112152
112598
|
api.post("/probe", async (request3, reply) => {
|
|
112153
112599
|
const parsed = labRequestSchema.safeParse(request3.body);
|
|
112154
112600
|
if (!parsed.success) {
|
|
@@ -112201,9 +112647,11 @@ async function routes(api) {
|
|
|
112201
112647
|
}
|
|
112202
112648
|
const key = skip.join(",");
|
|
112203
112649
|
if (!localCache || localCache.key !== key || Date.now() - localCache.at > 2e3) {
|
|
112204
|
-
|
|
112650
|
+
const entry = { at: Number.POSITIVE_INFINITY, key, servers: findLocalServers(skip) };
|
|
112651
|
+
void entry.servers.finally(() => entry.at = Date.now());
|
|
112652
|
+
localCache = entry;
|
|
112205
112653
|
}
|
|
112206
|
-
return { servers: localCache.servers };
|
|
112654
|
+
return { servers: await localCache.servers };
|
|
112207
112655
|
});
|
|
112208
112656
|
registerSuiteRoutes(api, workspace, suitePlans, (port2) => ({ practiceApi: `http://127.0.0.1:${port2}${api.prefix}/demo-api` }));
|
|
112209
112657
|
onRunFinished(async (report, error51) => {
|
|
@@ -112304,7 +112752,8 @@ init_src();
|
|
|
112304
112752
|
init_draft();
|
|
112305
112753
|
init_history_store();
|
|
112306
112754
|
init_k6();
|
|
112307
|
-
import { spawn as spawn3 } from "node:child_process";
|
|
112755
|
+
import { spawn as spawn3, spawnSync } from "node:child_process";
|
|
112756
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
112308
112757
|
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
112309
112758
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync as readdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
112310
112759
|
import { readFile as readFile10, writeFile as writeFile9 } from "node:fs/promises";
|
|
@@ -112393,11 +112842,12 @@ init_cookies();
|
|
|
112393
112842
|
init_workspace_store();
|
|
112394
112843
|
var repoRoot = resolve4(dirname3(fileURLToPath2(import.meta.url)), "../../..");
|
|
112395
112844
|
var EXIT = { passed: 0, failed: 1, error: 2 };
|
|
112396
|
-
var VERSION = "0.
|
|
112845
|
+
var VERSION = "0.10.0";
|
|
112397
112846
|
var HELP = `APITestCenter CLI ${VERSION} \u2014 https://apitestcenter.com/docs/cli-and-ci.html
|
|
112398
112847
|
|
|
112399
112848
|
Usage (apitestcenter, or the short name atc once installed)
|
|
112400
|
-
atc ui [collection] Open the app in
|
|
112849
|
+
atc ui [collection] Open the app in a window to build and edit tests
|
|
112850
|
+
(--browser: a tab in your usual browser; --no-open: just print the address)
|
|
112401
112851
|
atc init [collection] Start api-tests/ in this repository, with a CI pipeline
|
|
112402
112852
|
atc suite <collection> [options] Send every request in a collection once and check each one
|
|
112403
112853
|
atc run <request> [options] Run a load test and exit 0 (passed) / 1 (failed) / 2 (error)
|
|
@@ -112482,7 +112932,8 @@ async function main() {
|
|
|
112482
112932
|
help: { type: "boolean", short: "h" },
|
|
112483
112933
|
version: { type: "boolean", short: "v" },
|
|
112484
112934
|
data: { type: "string" },
|
|
112485
|
-
"no-open": { type: "boolean" }
|
|
112935
|
+
"no-open": { type: "boolean" },
|
|
112936
|
+
browser: { type: "boolean" }
|
|
112486
112937
|
}
|
|
112487
112938
|
});
|
|
112488
112939
|
const [command, target] = positionals;
|
|
@@ -112500,7 +112951,7 @@ async function main() {
|
|
|
112500
112951
|
}
|
|
112501
112952
|
if (command === "init") return init(target, values.data ?? "api-tests");
|
|
112502
112953
|
const dataDir = resolveDataDir();
|
|
112503
|
-
if (command === "ui") return ui(target, dataDir, values["no-open"] === true);
|
|
112954
|
+
if (command === "ui") return ui(target, dataDir, values["no-open"] === true, values.browser === true);
|
|
112504
112955
|
const secrets2 = new SecretStore(dataDir);
|
|
112505
112956
|
const workspace = new WorkspaceStore(dataDir, secrets2);
|
|
112506
112957
|
useOAuthStore(secrets2);
|
|
@@ -112618,10 +113069,10 @@ Environments (${index.environments.length})`);
|
|
|
112618
113069
|
}
|
|
112619
113070
|
if (payload.data) log(`data ${payload.data.fileName} \xB7 ${payload.data.rows.length} rows \xB7 ${payload.data.order}`);
|
|
112620
113071
|
if (!values["no-check"]) {
|
|
112621
|
-
const
|
|
112622
|
-
const failedChecks =
|
|
112623
|
-
log(`check ${
|
|
112624
|
-
if (!
|
|
113072
|
+
const probe2 = await runProbe({ request: payload.request, auth: payload.auth, options: payload.options, setup: payload.setup, data: payload.data });
|
|
113073
|
+
const failedChecks = probe2.checks?.filter((c) => !c.ok) ?? [];
|
|
113074
|
+
log(`check ${probe2.status || "ERR"} in ${probe2.durationMs} ms${probe2.checks ? ` \xB7 checks ${probe2.checks.length - failedChecks.length}/${probe2.checks.length}` : ""}`);
|
|
113075
|
+
if (!probe2.ok || probe2.status < 200 || probe2.status >= 300) fail(`check request returned ${probe2.status || probe2.error}; load did not start (use --no-check to force)`);
|
|
112625
113076
|
}
|
|
112626
113077
|
const history2 = values.save ? new HistoryStore(dataDir) : null;
|
|
112627
113078
|
if (history2) onRunFinished(async (report2, error51) => void await history2.saveRun(report2, { source: "run", error: error51 }));
|
|
@@ -112684,9 +113135,9 @@ compared with baseline \u201C${baseline.testName}\u201D (${baseline.startedAt.sl
|
|
|
112684
113135
|
if (history2) log(` saved to History in ${dataDir}`);
|
|
112685
113136
|
return failed ? EXIT.failed : EXIT.passed;
|
|
112686
113137
|
}
|
|
112687
|
-
async function ui(collection, dataDir, noOpen) {
|
|
113138
|
+
async function ui(collection, dataDir, noOpen, inBrowserTab = false) {
|
|
112688
113139
|
const here = dirname3(fileURLToPath2(import.meta.url));
|
|
112689
|
-
const uiDir2 = [resolve4(here, "ui"), resolve4(repoRoot, "apps/web/.next-desktop")].find((d) => existsSync2(resolve4(d, "index.html")));
|
|
113140
|
+
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")));
|
|
112690
113141
|
if (!uiDir2) fail("this build has no app screens. Use the npm package (npx apitestcenter ui), or run: pnpm --filter @apitestcenter/desktop build:ui");
|
|
112691
113142
|
Object.assign(process.env, {
|
|
112692
113143
|
ATC_UI_DIR: uiDir2,
|
|
@@ -112697,7 +113148,9 @@ async function ui(collection, dataDir, noOpen) {
|
|
|
112697
113148
|
ATC_USAGE_URL: "",
|
|
112698
113149
|
ATC_NO_DOTENV: "1",
|
|
112699
113150
|
ATC_NO_READY_LINE: "1",
|
|
112700
|
-
ATC_LOG_LEVEL: process.env.ATC_LOG_LEVEL ?? "warn"
|
|
113151
|
+
ATC_LOG_LEVEL: process.env.ATC_LOG_LEVEL ?? "warn",
|
|
113152
|
+
// The screens show "stopped" when this engine goes away (a browser tab can't be closed for them).
|
|
113153
|
+
ATC_HOST: "cli"
|
|
112701
113154
|
});
|
|
112702
113155
|
if (!(await resolveK6Bin(repoRoot)).available) {
|
|
112703
113156
|
const cached3 = cachedK6();
|
|
@@ -112712,10 +113165,54 @@ async function ui(collection, dataDir, noOpen) {
|
|
|
112712
113165
|
console.log(` tests in: ${dataDir}`);
|
|
112713
113166
|
console.log(" Saved secrets last until you stop it \u2014 use {{variables}} for tests you commit.");
|
|
112714
113167
|
console.log(" Press Ctrl+C to stop.");
|
|
112715
|
-
if (!noOpen)
|
|
113168
|
+
if (!noOpen) {
|
|
113169
|
+
const win = inBrowserTab ? null : openAppWindow(page2);
|
|
113170
|
+
if (!win) openBrowser(page2);
|
|
113171
|
+
else {
|
|
113172
|
+
console.log(" Closing the window stops it too.");
|
|
113173
|
+
const stop = (why) => {
|
|
113174
|
+
if (why) console.log(why);
|
|
113175
|
+
closeWindow(win);
|
|
113176
|
+
process.exit(EXIT.passed);
|
|
113177
|
+
};
|
|
113178
|
+
process.on("SIGINT", () => stop("\nStopped."));
|
|
113179
|
+
process.on("SIGTERM", () => stop(""));
|
|
113180
|
+
win.on("exit", () => {
|
|
113181
|
+
console.log("Window closed \u2014 stopped.");
|
|
113182
|
+
process.exit(EXIT.passed);
|
|
113183
|
+
});
|
|
113184
|
+
}
|
|
113185
|
+
}
|
|
112716
113186
|
return new Promise(() => {
|
|
112717
113187
|
});
|
|
112718
113188
|
}
|
|
113189
|
+
function findAppBrowser() {
|
|
113190
|
+
const env = process.env;
|
|
113191
|
+
const candidates = process.platform === "win32" ? [env["ProgramFiles(x86)"], env.ProgramFiles, env.LOCALAPPDATA].flatMap(
|
|
113192
|
+
(root2) => root2 ? [`${root2}\\Microsoft\\Edge\\Application\\msedge.exe`, `${root2}\\Google\\Chrome\\Application\\chrome.exe`, `${root2}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe`] : []
|
|
113193
|
+
) : 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"];
|
|
113194
|
+
return candidates.find((p) => existsSync2(p)) ?? null;
|
|
113195
|
+
}
|
|
113196
|
+
function openAppWindow(url2) {
|
|
113197
|
+
const bin = findAppBrowser();
|
|
113198
|
+
if (!bin) return null;
|
|
113199
|
+
const profile = resolve4(tmpdir3(), "apitestcenter-ui-window");
|
|
113200
|
+
try {
|
|
113201
|
+
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 });
|
|
113202
|
+
child.on("error", () => openBrowser(url2));
|
|
113203
|
+
return child;
|
|
113204
|
+
} catch {
|
|
113205
|
+
return null;
|
|
113206
|
+
}
|
|
113207
|
+
}
|
|
113208
|
+
function closeWindow(win) {
|
|
113209
|
+
if (win.exitCode !== null || !win.pid) return;
|
|
113210
|
+
try {
|
|
113211
|
+
if (process.platform === "win32") spawnSync("taskkill", ["/pid", String(win.pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
|
|
113212
|
+
else win.kill("SIGTERM");
|
|
113213
|
+
} catch {
|
|
113214
|
+
}
|
|
113215
|
+
}
|
|
112719
113216
|
function openBrowser(url2) {
|
|
112720
113217
|
const [cmd, args] = process.platform === "win32" ? ["cmd", ["/c", "start", "", url2]] : process.platform === "darwin" ? ["open", [url2]] : ["xdg-open", [url2]];
|
|
112721
113218
|
try {
|