@requesto/cli 1.13.1 → 1.14.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.
Files changed (2) hide show
  1. package/dist/index.mjs +178 -37
  2. package/package.json +3 -3
package/dist/index.mjs CHANGED
@@ -106618,6 +106618,7 @@ var require_proxy_service = __commonJS({
106618
106618
  validateStatus: () => true,
106619
106619
  timeout: opts.timeout ?? 3e4,
106620
106620
  responseType: "arraybuffer",
106621
+ ...opts.signal && { signal: opts.signal },
106621
106622
  ...httpsAgent && { httpsAgent }
106622
106623
  };
106623
106624
  const axiosAuth = (0, auth_1.getAxiosAuthConfig)(substitutedAuth);
@@ -134532,6 +134533,9 @@ function escapeXml(text) {
134532
134533
  function seconds(ms) {
134533
134534
  return ((ms ?? 0) / 1e3).toFixed(3);
134534
134535
  }
134536
+ function skippedTestcaseCount(results) {
134537
+ return results.filter((r2) => r2.status === "skipped" || r2.status === "warned").length;
134538
+ }
134535
134539
  function testcase(result, indent2) {
134536
134540
  const classname = escapeXml(result.collectionName);
134537
134541
  const lines = [];
@@ -134553,6 +134557,15 @@ function testcase(result, indent2) {
134553
134557
  );
134554
134558
  return lines;
134555
134559
  }
134560
+ if (result.status === "warned") {
134561
+ const message = result.warning ?? "Warning";
134562
+ lines.push(
134563
+ `${indent2}<testcase name="${escapeXml(`Request: ${result.request.name}`)}" classname="${classname}" time="${seconds(result.duration)}">`,
134564
+ `${indent2} <skipped message="${escapeXml(message)}"/>`,
134565
+ `${indent2}</testcase>`
134566
+ );
134567
+ return lines;
134568
+ }
134556
134569
  if (result.testResults.length === 0) {
134557
134570
  lines.push(
134558
134571
  `${indent2}<testcase name="Request: ${escapeXml(result.request.name)}" classname="${classname}" time="${seconds(result.duration)}"/>`
@@ -134594,7 +134607,7 @@ function buildJunitReport(summary) {
134594
134607
  const time = seconds(results.reduce((acc, r2) => acc + (r2.duration ?? 0), 0));
134595
134608
  suites.push(
134596
134609
  [
134597
- ` <testsuite name="${escapeXml(collectionName)}" tests="${tests}" failures="${failures}" errors="${errors}" time="${time}">`,
134610
+ ` <testsuite name="${escapeXml(collectionName)}" tests="${tests}" failures="${failures}" errors="${errors}" skipped="${skippedTestcaseCount(results)}" time="${time}">`,
134598
134611
  ...results.flatMap((r2) => testcase(r2, " ")),
134599
134612
  ` </testsuite>`
134600
134613
  ].join("\n")
@@ -134611,7 +134624,7 @@ function buildJunitReport(summary) {
134611
134624
  const totalErrors = summary.results.filter((r2) => r2.status === "error").length;
134612
134625
  return [
134613
134626
  `<?xml version="1.0" encoding="UTF-8"?>`,
134614
- `<testsuites name="requesto" tests="${totalTests}" failures="${totalFailures}" errors="${totalErrors}" time="${seconds(summary.totalDuration)}">`,
134627
+ `<testsuites name="requesto" tests="${totalTests}" failures="${totalFailures}" errors="${totalErrors}" skipped="${skippedTestcaseCount(summary.results)}" time="${seconds(summary.totalDuration)}">`,
134615
134628
  ...suites,
134616
134629
  `</testsuites>`,
134617
134630
  ``
@@ -134627,6 +134640,7 @@ function buildJsonReport(summary) {
134627
134640
  passed: summary.passed,
134628
134641
  failed: summary.failed,
134629
134642
  errored: summary.errored,
134643
+ warned: summary.warned,
134630
134644
  skipped: summary.skipped
134631
134645
  },
134632
134646
  tests: {
@@ -134648,6 +134662,8 @@ function buildJsonReport(summary) {
134648
134662
  httpStatus: result.response?.status ?? null,
134649
134663
  durationMs: result.duration ?? null,
134650
134664
  error: result.error ?? null,
134665
+ warning: result.warning ?? null,
134666
+ warningKind: result.warningKind ?? null,
134651
134667
  tests: result.testResults.map((test) => ({
134652
134668
  name: test.name,
134653
134669
  passed: test.passed,
@@ -134676,6 +134692,8 @@ function statusIcon(result, paint) {
134676
134692
  return paint.green("\u2713");
134677
134693
  case "failed":
134678
134694
  return paint.red("\u2717");
134695
+ case "warned":
134696
+ return paint.yellow("\u26A0");
134679
134697
  case "error":
134680
134698
  return paint.yellow("\u2717");
134681
134699
  case "skipped":
@@ -134688,6 +134706,8 @@ function dotChar(result, paint) {
134688
134706
  return paint.green("\xB7");
134689
134707
  case "failed":
134690
134708
  return paint.red("\xD7");
134709
+ case "warned":
134710
+ return paint.yellow("!");
134691
134711
  case "error":
134692
134712
  return paint.yellow("\xD7");
134693
134713
  case "skipped":
@@ -134732,6 +134752,10 @@ function createConsoleReporter(mode, stream = process.stdout) {
134732
134752
  const writeRequestLine = (result) => {
134733
134753
  stream.write(` ${statusIcon(result, paint)} ${result.request.name}${paint.dim(durationTail(result))}
134734
134754
  `);
134755
+ if (result.status === "warned" && result.warning) {
134756
+ stream.write(paint.yellow(` \u26A0 ${result.warning}
134757
+ `));
134758
+ }
134735
134759
  };
134736
134760
  const failureEntries = (summary) => summary.results.filter((r2) => r2.status === "failed" || r2.status === "error").map((r2) => ({
134737
134761
  label: `${r2.collectionName} > ${r2.request.name}`,
@@ -134753,10 +134777,11 @@ function createConsoleReporter(mode, stream = process.stdout) {
134753
134777
  }
134754
134778
  };
134755
134779
  const writeSummary = (summary, meta) => {
134756
- const failed = summary.failed + summary.errored;
134757
134780
  const counts = [
134758
134781
  summary.passed > 0 ? paint.green(`${summary.passed} passed`) : null,
134759
- failed > 0 ? paint.red(`${failed} failed`) : null,
134782
+ summary.failed > 0 ? paint.red(`${summary.failed} failed`) : null,
134783
+ summary.errored > 0 ? paint.yellow(`${summary.errored} errored`) : null,
134784
+ summary.warned > 0 ? paint.yellow(`${summary.warned} warnings`) : null,
134760
134785
  summary.skipped > 0 ? paint.dim(`${summary.skipped} skipped`) : null
134761
134786
  ].filter(Boolean);
134762
134787
  stream.write("\n");
@@ -134894,16 +134919,12 @@ function buildSavedGraphQLRequest(req) {
134894
134919
  if (operation.operation !== "query") {
134895
134920
  throw new Error("GraphQL GET requests can only execute query operations");
134896
134921
  }
134897
- const hashIndex = req.url.indexOf("#");
134898
- const base = hashIndex < 0 ? req.url : req.url.slice(0, hashIndex);
134899
- const hash = hashIndex < 0 ? "" : req.url.slice(hashIndex);
134900
- const queryIndex = base.indexOf("?");
134901
- const path6 = queryIndex < 0 ? base : base.slice(0, queryIndex);
134902
- const params = new URLSearchParams(queryIndex < 0 ? "" : base.slice(queryIndex + 1));
134903
- params.set("query", document2);
134904
- if (variables)
134905
- params.set("variables", JSON.stringify(variables));
134906
- return { method: "GET", url: `${path6}?${params}${hash}`, headers, auth: req.auth };
134922
+ return {
134923
+ method: "GET",
134924
+ url: encodeGraphQLGetUrl(req.url, document2, variables),
134925
+ headers,
134926
+ auth: req.auth
134927
+ };
134907
134928
  }
134908
134929
  setDefaultHeader(headers, "Content-Type", "application/json");
134909
134930
  const body = {
@@ -134919,6 +134940,18 @@ function buildSavedGraphQLRequest(req) {
134919
134940
  auth: req.auth
134920
134941
  };
134921
134942
  }
134943
+ function encodeGraphQLGetUrl(base, document2, variables) {
134944
+ const hashIndex = base.indexOf("#");
134945
+ const withoutHash = hashIndex < 0 ? base : base.slice(0, hashIndex);
134946
+ const hash = hashIndex < 0 ? "" : base.slice(hashIndex);
134947
+ const queryIndex = withoutHash.indexOf("?");
134948
+ const path6 = queryIndex < 0 ? withoutHash : withoutHash.slice(0, queryIndex);
134949
+ const params = new URLSearchParams(queryIndex < 0 ? "" : withoutHash.slice(queryIndex + 1));
134950
+ params.set("query", document2);
134951
+ if (variables)
134952
+ params.set("variables", JSON.stringify(variables));
134953
+ return `${path6}?${params}${hash}`;
134954
+ }
134922
134955
  function buildProxyRequest(req, insecureTls) {
134923
134956
  if (req.requestType === "graphql") {
134924
134957
  return { ...buildSavedGraphQLRequest(req), insecureTls };
@@ -134935,6 +134968,31 @@ function buildProxyRequest(req, insecureTls) {
134935
134968
  };
134936
134969
  }
134937
134970
 
134971
+ // ../../packages/engine/dist/request/graphql-envelope.js
134972
+ function getGraphQLResponseInfo(response) {
134973
+ if (response.bodyEncoding !== "utf8")
134974
+ return null;
134975
+ try {
134976
+ const payload = JSON.parse(response.body);
134977
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload))
134978
+ return null;
134979
+ const envelope = payload;
134980
+ if (!Array.isArray(envelope.errors))
134981
+ return null;
134982
+ const errors = envelope.errors.filter((error) => typeof error === "object" && error !== null).map((error) => ({
134983
+ message: typeof error.message === "string" ? error.message : "Unknown GraphQL error",
134984
+ ...Array.isArray(error.path) && { path: error.path },
134985
+ ...Array.isArray(error.locations) && {
134986
+ locations: error.locations.filter((location2) => typeof location2 === "object" && location2 !== null && typeof location2.line === "number" && typeof location2.column === "number")
134987
+ }
134988
+ }));
134989
+ const hasData = Object.prototype.hasOwnProperty.call(envelope, "data") && envelope.data !== null;
134990
+ return { errors, hasData, isPartial: hasData && errors.length > 0 };
134991
+ } catch {
134992
+ return null;
134993
+ }
134994
+ }
134995
+
134938
134996
  // ../../packages/engine/dist/runner/display.js
134939
134997
  function buildCollectionItems(collection, folderIds) {
134940
134998
  const items = [];
@@ -135002,6 +135060,31 @@ function applyEnvOverrides(env2, overrides) {
135002
135060
  }
135003
135061
  return { ...env2, variables };
135004
135062
  }
135063
+ function resolveStatus(allTestsPassed, testCount, response, warnings) {
135064
+ if (!allTestsPassed)
135065
+ return { status: "failed" };
135066
+ const warningsEnabled = warnings?.httpErrorStatuses !== false;
135067
+ const hasTests = testCount > 0;
135068
+ if (warningsEnabled && !hasTests) {
135069
+ if (response.status >= 400) {
135070
+ const statusText = response.statusText?.trim();
135071
+ return {
135072
+ status: "warned",
135073
+ warning: statusText ? `HTTP ${response.status} ${statusText}` : `HTTP ${response.status}`,
135074
+ warningKind: "http-status"
135075
+ };
135076
+ }
135077
+ const graphqlInfo = getGraphQLResponseInfo(response);
135078
+ if (graphqlInfo && graphqlInfo.errors.length > 0) {
135079
+ return {
135080
+ status: "warned",
135081
+ warning: graphqlInfo.errors[0].message,
135082
+ warningKind: "graphql-errors"
135083
+ };
135084
+ }
135085
+ }
135086
+ return { status: "passed" };
135087
+ }
135005
135088
  async function runCollections(opts) {
135006
135089
  const results = [];
135007
135090
  const started = Date.now();
@@ -135085,8 +135168,16 @@ async function runCollections(opts) {
135085
135168
  liveEnv = applyEnvOverrides(liveEnv, outcome);
135086
135169
  }
135087
135170
  const allPassed = testResults.every((t2) => t2.passed);
135088
- const status = allPassed ? "passed" : "failed";
135089
- const result = { ...base, status, response, testResults, duration: response.duration };
135171
+ const { status, warning, warningKind } = resolveStatus(allPassed, testResults.length, response, opts.warnings);
135172
+ const result = {
135173
+ ...base,
135174
+ status,
135175
+ response,
135176
+ testResults,
135177
+ ...warning !== void 0 && { warning },
135178
+ ...warningKind !== void 0 && { warningKind },
135179
+ duration: response.duration
135180
+ };
135090
135181
  results.push(result);
135091
135182
  opts.onEvent?.({ type: "request-end", result });
135092
135183
  if (!allPassed && opts.bail)
@@ -135113,6 +135204,7 @@ async function runCollections(opts) {
135113
135204
  passed: results.filter((r2) => r2.status === "passed").length,
135114
135205
  failed: results.filter((r2) => r2.status === "failed").length,
135115
135206
  errored: results.filter((r2) => r2.status === "error").length,
135207
+ warned: results.filter((r2) => r2.status === "warned").length,
135116
135208
  skipped: results.filter((r2) => r2.status === "skipped").length,
135117
135209
  totalTests: results.reduce((acc, r2) => acc + r2.testResults.length, 0),
135118
135210
  passedTests: results.reduce((acc, r2) => acc + r2.testResults.filter((t2) => t2.passed).length, 0),
@@ -135573,7 +135665,11 @@ var CliWorkspace = class {
135573
135665
  available ? `Environment "${selector}" not found. Available: ${available} (or "none").` : `Environment "${selector}" not found: this workspace has no environments (use "none" or define one).`
135574
135666
  );
135575
135667
  }
135576
- /** Send a request through the backend engine with the given per-run options. */
135668
+ /**
135669
+ * Send a request through the backend engine with the given per-run options.
135670
+ * The runner-provided abort signal is forwarded so an interrupted run can
135671
+ * cancel the in-flight HTTP request instead of waiting for its timeout.
135672
+ */
135577
135673
  sendRequest(request, opts) {
135578
135674
  return this.proxyService.executeRequest(request, {
135579
135675
  // The CLI substitutes variables itself against the live environment,
@@ -135581,6 +135677,7 @@ var CliWorkspace = class {
135581
135677
  environment: null,
135582
135678
  oauthResolver: opts.oauthResolver,
135583
135679
  timeout: opts.timeout,
135680
+ signal: opts.signal,
135584
135681
  saveHistory: false
135585
135682
  });
135586
135683
  }
@@ -135607,6 +135704,34 @@ function resolveWorkspacePath(input) {
135607
135704
 
135608
135705
  // src/commands/run.ts
135609
135706
  var BUILTIN_SERVER_VAR = "requestoServerUrl";
135707
+ function wireAbortSignal(opts) {
135708
+ const controller = new AbortController();
135709
+ const externalSignal = opts.signal ?? null;
135710
+ let interrupts = 0;
135711
+ const handleInterrupt = () => {
135712
+ interrupts += 1;
135713
+ if (interrupts > 1) {
135714
+ process.stderr.write("\nForcing exit (second interrupt).\n");
135715
+ process.exit(130);
135716
+ }
135717
+ controller.abort();
135718
+ process.stderr.write(
135719
+ "\nInterrupted: cancelling the in-flight request and skipping the rest of the run. Press Ctrl-C again to force exit.\n"
135720
+ );
135721
+ };
135722
+ const handleExternalAbort = () => {
135723
+ controller.abort(externalSignal?.reason);
135724
+ };
135725
+ process.on("SIGINT", handleInterrupt);
135726
+ externalSignal?.addEventListener("abort", handleExternalAbort);
135727
+ return {
135728
+ signal: controller.signal,
135729
+ dispose: () => {
135730
+ process.off("SIGINT", handleInterrupt);
135731
+ externalSignal?.removeEventListener("abort", handleExternalAbort);
135732
+ }
135733
+ };
135734
+ }
135610
135735
  async function runCommand(opts) {
135611
135736
  const started = Date.now();
135612
135737
  const workspacePath = resolveWorkspacePath(opts.path);
@@ -135658,6 +135783,7 @@ async function runCommand(opts) {
135658
135783
  serverUrl = embedded.url;
135659
135784
  }
135660
135785
  const runEnv = withBuiltinServerVar(effectiveEnv, serverUrl);
135786
+ const abort = wireAbortSignal(opts);
135661
135787
  try {
135662
135788
  const summary = await runCollections({
135663
135789
  collections,
@@ -135670,6 +135796,8 @@ async function runCommand(opts) {
135670
135796
  bail: opts.bail,
135671
135797
  insecure: opts.insecure,
135672
135798
  timeout: opts.timeout,
135799
+ signal: abort.signal,
135800
+ warnings: { httpErrorStatuses: !opts.noWarnHttpErrors },
135673
135801
  onEvent: opts.onEvent
135674
135802
  });
135675
135803
  return {
@@ -135679,6 +135807,7 @@ async function runCommand(opts) {
135679
135807
  serverUrl
135680
135808
  };
135681
135809
  } finally {
135810
+ abort.dispose();
135682
135811
  if (embedded) {
135683
135812
  try {
135684
135813
  await embedded.stop();
@@ -135697,8 +135826,9 @@ async function runCommand(opts) {
135697
135826
  }
135698
135827
  }
135699
135828
  }
135700
- function computeExitCode(summary) {
135701
- return summary.failed + summary.errored > 0 ? 1 : 0;
135829
+ function computeExitCode(summary, opts = {}) {
135830
+ const failing = summary.failed + summary.errored + (opts.failOnWarn ? summary.warned : 0);
135831
+ return failing > 0 ? 1 : 0;
135702
135832
  }
135703
135833
  function referencesBuiltin(env2) {
135704
135834
  if (!env2) return false;
@@ -135780,6 +135910,26 @@ function writeFileReports(specs, result) {
135780
135910
  `);
135781
135911
  }
135782
135912
  }
135913
+ function collectRunOptions(pathArg, opts) {
135914
+ return {
135915
+ path: pathArg,
135916
+ collections: opts.collection,
135917
+ excludeCollections: opts.excludeCollection,
135918
+ folders: opts.folder,
135919
+ environment: opts.environment,
135920
+ vars: opts.var,
135921
+ varFile: opts.varFile,
135922
+ tokens: opts.token,
135923
+ oauthSecrets: opts.oauthSecret,
135924
+ refreshTokens: opts.refreshToken,
135925
+ bail: opts.bail,
135926
+ insecure: opts.insecure,
135927
+ timeout: opts.timeout,
135928
+ persist: opts.persist,
135929
+ server: opts.server,
135930
+ noWarnHttpErrors: opts.warnHttpErrors === false
135931
+ };
135932
+ }
135783
135933
  async function runCli(argv) {
135784
135934
  const program2 = new Command();
135785
135935
  program2.exitOverride();
@@ -135795,31 +135945,22 @@ The run never touches your workspace or any server data: collections targeting t
135795
135945
  {{${BUILTIN_SERVER_VAR}}} variable run against an ephemeral scratch server that the CLI
135796
135946
  boots and tears down itself. Use --server to test a deployed server instead.
135797
135947
 
135798
- Exit codes: 0 = all passed, 1 = failures/errors, 2 = configuration error.`
135948
+ Exit codes: 0 = all passed, 1 = failures/errors, 2 = configuration error.
135949
+
135950
+ 4xx/5xx responses with no test assertions land as warnings: they print as \u26A0 and
135951
+ never affect the exit code unless --fail-on-warn is given. Ctrl-C cancels the
135952
+ in-flight request and skips the rest; press again to exit immediately.`
135799
135953
  ).argument("[path]", "repository or .requesto directory", process.cwd()).option("-c, --collection <name>", "collection to run (name or id, repeatable; default: all)", collect, []).option("-C, --exclude-collection <name>", "collection to skip (name or id, repeatable; subtracts from the selection)", collect, []).option("-f, --folder <name>", "folder to run within the selected collections (name or id, repeatable)", collect, []).option("-e, --environment <name>", 'environment (name or id, or "none"; default: active environment)').option("--var <key=value>", "variable override (repeatable; highest precedence)", collect, []).option("--var-file <path>", ".env file providing variable overrides").option("--token <configId=accessToken>", "access token for an OAuth config, e.g. --token my-entra=eyJ... (repeatable; REQUESTO_TOKEN_* env vars work too)", collect, []).option("--oauth-secret <configId=secret>", "client secret for a non-interactive OAuth config (repeatable; REQUESTO_OAUTH_SECRET_* env vars work too)", collect, []).option("--refresh-token <configId=token>", "refresh token to seed for an OAuth config (repeatable; REQUESTO_REFRESH_TOKEN_* env vars work too)", collect, []).option("-x, --bail", "stop the run after the first failed or errored request", false).option("--insecure", "skip TLS certificate verification (self-signed certs)", false).option("--timeout <ms>", "per-request timeout in ms (default 30000)", (v) => {
135800
135954
  const n = Number(v);
135801
135955
  if (!Number.isFinite(n) || n <= 0) throw new CliError(`Invalid --timeout value "${v}"`);
135802
135956
  return n;
135803
- }).option("--persist", "persist OAuth tokens acquired during the run to .requesto/local/ (default: memory only)", false).option("--server <url>", "test a deployed Requesto server instead of the embedded scratch server (its active workspace is protected by a scratch workspace)").option("--reporter <spec>", "reporter: console, verbose, dot, junit:<path> or json:<path> (repeatable; default: console)", collect, []).action(async (pathArg, opts) => {
135957
+ }).option("--persist", "persist OAuth tokens acquired during the run to .requesto/local/ (default: memory only)", false).option("--server <url>", "test a deployed Requesto server instead of the embedded scratch server (its active workspace is protected by a scratch workspace)").option("--no-warn-http-errors", "disable warn-tier results for 4xx/5xx responses with no test assertions (default: warns are on)").option("--fail-on-warn", "exit 1 when any request is warned (default: warns never affect the exit code)", false).option("--reporter <spec>", "reporter: console, verbose, dot, junit:<path> or json:<path> (repeatable; default: console)", collect, []).action(async (pathArg, opts) => {
135804
135958
  const specs = opts.reporter.map(parseReporterSpec);
135805
135959
  const consoleMode = findConsoleMode(specs);
135806
135960
  const consoleReporter = createConsoleReporter(consoleMode);
135961
+ const runOptions = collectRunOptions(pathArg, opts);
135807
135962
  const result = await runCommand({
135808
- path: pathArg,
135809
- collections: opts.collection,
135810
- excludeCollections: opts.excludeCollection,
135811
- folders: opts.folder,
135812
- environment: opts.environment,
135813
- vars: opts.var,
135814
- varFile: opts.varFile,
135815
- tokens: opts.token,
135816
- oauthSecrets: opts.oauthSecret,
135817
- refreshTokens: opts.refreshToken,
135818
- bail: opts.bail,
135819
- insecure: opts.insecure,
135820
- timeout: opts.timeout,
135821
- persist: opts.persist,
135822
- server: opts.server,
135963
+ ...runOptions,
135823
135964
  onEvent: (event) => consoleReporter.onEvent(event)
135824
135965
  });
135825
135966
  consoleReporter.finish(result.summary, {
@@ -135828,7 +135969,7 @@ Exit codes: 0 = all passed, 1 = failures/errors, 2 = configuration error.`
135828
135969
  serverUrl: result.serverUrl
135829
135970
  });
135830
135971
  writeFileReports(specs, result);
135831
- exitCode = computeExitCode(result.summary);
135972
+ exitCode = computeExitCode(result.summary, { failOnWarn: opts.failOnWarn });
135832
135973
  });
135833
135974
  await program2.parseAsync(argv, { from: "user" });
135834
135975
  return exitCode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@requesto/cli",
3
- "version": "1.13.1",
3
+ "version": "1.14.0",
4
4
  "description": "Run your Requesto collections from the terminal and CI pipelines",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -26,8 +26,8 @@
26
26
  "devDependencies": {
27
27
  "@types/node": "^26.6.4",
28
28
  "esbuild": "^0.28.2",
29
- "requesto-backend": "1.13.1",
30
- "requesto-engine": "1.13.1",
29
+ "requesto-backend": "1.14.0",
30
+ "requesto-engine": "1.14.0",
31
31
  "tsx": "^4.23.15",
32
32
  "typescript": "^7.0.2",
33
33
  "vitest": "^5.0.3"