@redocly/cli 2.36.0 → 2.38.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.
@@ -0,0 +1,129 @@
1
+ import { createRequire as __createRequire } from 'node:module';
2
+ const require = __createRequire(import.meta.url);
3
+ import {
4
+ initRules,
5
+ require_graphql
6
+ } from "./Z2I5YXYN.js";
7
+ import {
8
+ __toESM
9
+ } from "./5ILQMFXK.js";
10
+
11
+ // ../core/lib/graphql/lint-graphql.js
12
+ var import_graphql2 = __toESM(require_graphql(), 1);
13
+
14
+ // ../core/lib/graphql/visitor.js
15
+ var import_graphql = __toESM(require_graphql(), 1);
16
+ function toAstVisitor(visitor, opts) {
17
+ const astVisitor = {};
18
+ for (const kind of Object.keys(visitor)) {
19
+ const handler = visitor[kind];
20
+ if (!handler)
21
+ continue;
22
+ const enter = typeof handler === "function" ? handler : handler.enter;
23
+ const leave = typeof handler === "function" ? void 0 : handler.leave;
24
+ astVisitor[kind] = {
25
+ ...enter ? { enter: wrap(enter, opts) } : {},
26
+ ...leave ? { leave: wrap(leave, opts) } : {}
27
+ };
28
+ }
29
+ return astVisitor;
30
+ }
31
+ function wrap(fn, opts) {
32
+ return (node, _key, _parent, _path, ancestors) => {
33
+ fn(node, makeContext(node, opts, ancestors));
34
+ };
35
+ }
36
+ function makeContext(currentNode, opts, rawAncestors = []) {
37
+ const { ruleId, severity, message, source, config, problems } = opts;
38
+ const ancestors = rawAncestors.filter((ancestor) => !Array.isArray(ancestor));
39
+ return {
40
+ source,
41
+ config,
42
+ ancestors,
43
+ report(problem) {
44
+ const node = problem.node ?? currentNode;
45
+ const loc = problem.loc ?? nodeToLoc(node);
46
+ const location = {
47
+ source,
48
+ pointer: void 0,
49
+ start: loc?.start ?? { line: 1, col: 1 },
50
+ end: loc?.end
51
+ };
52
+ problems.push({
53
+ ruleId: problem.ruleId ?? ruleId,
54
+ severity: problem.severity ?? severity,
55
+ message: message ? message.replace("{{message}}", problem.message) : problem.message,
56
+ suggest: problem.suggest ?? [],
57
+ location: [location]
58
+ });
59
+ }
60
+ };
61
+ }
62
+ function nodeToLoc(node) {
63
+ if (!node?.loc)
64
+ return void 0;
65
+ const start = (0, import_graphql.getLocation)(node.loc.source, node.loc.start);
66
+ const end = (0, import_graphql.getLocation)(node.loc.source, node.loc.end);
67
+ return {
68
+ start: { line: start.line, col: start.column },
69
+ end: { line: end.line, col: end.column }
70
+ };
71
+ }
72
+
73
+ // ../core/lib/graphql/lint-graphql.js
74
+ function lintGraphqlDocument(opts) {
75
+ const { document, config } = opts;
76
+ const source = document.source;
77
+ if (source.body.trim() === "") {
78
+ return [
79
+ {
80
+ ruleId: "struct",
81
+ severity: "error",
82
+ message: "The GraphQL document is empty. Expected at least one type definition.",
83
+ suggest: [],
84
+ location: [{ source, pointer: void 0, start: { line: 1, col: 1 } }]
85
+ }
86
+ ];
87
+ }
88
+ let ast;
89
+ try {
90
+ ast = (0, import_graphql2.parse)(new import_graphql2.Source(source.body, source.absoluteRef));
91
+ } catch (e) {
92
+ if (e instanceof import_graphql2.GraphQLError) {
93
+ return [syntaxErrorToProblem(e, source)];
94
+ }
95
+ throw e;
96
+ }
97
+ const ruleSets = config.getRulesForSpecVersion("graphql") ?? [];
98
+ const rules = initRules(ruleSets, config, "rules", "graphql");
99
+ const problems = runGraphqlRules({ ast, source, config, rules });
100
+ return problems.map((problem) => config.addProblemToIgnore(problem));
101
+ }
102
+ function syntaxErrorToProblem(error, source) {
103
+ const loc = error.locations?.[0];
104
+ return {
105
+ ruleId: "struct",
106
+ severity: "error",
107
+ message: error.message,
108
+ suggest: [],
109
+ location: [
110
+ {
111
+ source,
112
+ pointer: void 0,
113
+ start: loc ? { line: loc.line, col: loc.column } : { line: 1, col: 1 }
114
+ }
115
+ ]
116
+ };
117
+ }
118
+ function runGraphqlRules(opts) {
119
+ const { ast, source, config, rules } = opts;
120
+ const problems = [];
121
+ const astVisitors = rules.map(({ ruleId, severity, message, visitor }) => toAstVisitor(visitor, { ruleId, severity, message, source, config, problems }));
122
+ if (astVisitors.length > 0) {
123
+ (0, import_graphql2.visit)(ast, (0, import_graphql2.visitInParallel)(astVisitors));
124
+ }
125
+ return problems;
126
+ }
127
+ export {
128
+ lintGraphqlDocument
129
+ };
package/lib/index.js CHANGED
@@ -6,12 +6,10 @@ import {
6
6
  ulid
7
7
  } from "./chunks/ICAVAH7N.js";
8
8
  import {
9
- AbortFlowError,
10
9
  capitalize,
11
10
  cleanColors,
12
11
  dumpBundle,
13
12
  escapeLanguageName,
14
- exitWithError,
15
13
  formatPath,
16
14
  getAliasOrPath,
17
15
  getAndValidateFileExtension,
@@ -30,7 +28,11 @@ import {
30
28
  saveBundle,
31
29
  sortTopLevelKeys,
32
30
  writeToFileByExtension
33
- } from "./chunks/R5E5LXAJ.js";
31
+ } from "./chunks/QUMOPKXU.js";
32
+ import {
33
+ AbortFlowError,
34
+ exitWithError
35
+ } from "./chunks/FL4Q7PMA.js";
34
36
  import {
35
37
  RedoclyOAuthClient,
36
38
  ReuniteApi,
@@ -39,26 +41,29 @@ import {
39
41
  getDomain,
40
42
  getProxyUrl,
41
43
  getReuniteUrl,
42
- require_undici,
43
44
  shouldBypassProxy
44
- } from "./chunks/FMJFZCNQ.js";
45
+ } from "./chunks/K5FO6VRS.js";
45
46
  import {
46
47
  ANONYMOUS_ID_CACHE_FILE,
47
48
  DEFAULT_FETCH_TIMEOUT,
48
49
  OTEL_URL
49
50
  } from "./chunks/Y6DTFCLS.js";
50
- import "./chunks/SHVJYD75.js";
51
+ import "./chunks/4FB5ZIPP.js";
51
52
  import {
52
53
  engines,
53
54
  name,
54
55
  version
55
- } from "./chunks/K6PX5SU7.js";
56
+ } from "./chunks/PIMMKQJ7.js";
57
+ import {
58
+ require_undici
59
+ } from "./chunks/XB6C62FW.js";
56
60
  import {
57
61
  RESET_ESCAPE_CODE,
58
62
  calculateTotals,
59
63
  conditionallyMaskSecrets
60
- } from "./chunks/35B2IXV3.js";
64
+ } from "./chunks/M5T4PDSH.js";
61
65
  import "./chunks/VXIQEZPR.js";
66
+ import "./chunks/UUU33DK3.js";
62
67
  import {
63
68
  BaseResolver,
64
69
  HandledError,
@@ -109,7 +114,8 @@ import {
109
114
  white,
110
115
  xmlEscape,
111
116
  yellow
112
- } from "./chunks/UVIPIXTL.js";
117
+ } from "./chunks/MIPR6NCD.js";
118
+ import "./chunks/Z2I5YXYN.js";
113
119
  import {
114
120
  __commonJS,
115
121
  __require,
@@ -7703,7 +7709,7 @@ async function handleGenerateArazzo({
7703
7709
  config: config3
7704
7710
  };
7705
7711
  try {
7706
- const { generate } = await import("./chunks/4QGHXAYN.js");
7712
+ const { generate } = await import("./chunks/EDHFTO4X.js");
7707
7713
  logger.info(gray("\n Generating Arazzo description... \n"));
7708
7714
  const generatedArazzo = await generate(options);
7709
7715
  writeFileSync2(outputFile, stringifyYaml(generatedArazzo));
@@ -9179,7 +9185,7 @@ async function handleRespect({
9179
9185
  let mtlsCerts;
9180
9186
  let harLogs;
9181
9187
  try {
9182
- const { run, conditionallyMaskSecrets: conditionallyMaskSecrets2 } = await import("./chunks/4QGHXAYN.js");
9188
+ const { run, conditionallyMaskSecrets: conditionallyMaskSecrets2 } = await import("./chunks/EDHFTO4X.js");
9183
9189
  const workingDir = config3.configPath ? dirname4(config3.configPath) : process.cwd();
9184
9190
  if (argv.mtls) {
9185
9191
  mtlsCerts = resolveMtlsCertificates(argv.mtls, workingDir);
@@ -12699,7 +12705,7 @@ async function sendTelemetry({
12699
12705
  $0: _,
12700
12706
  ...args
12701
12707
  } = argv;
12702
- const { RedoclyOAuthClient: RedoclyOAuthClient2 } = await import("./chunks/SBGJND3Q.js");
12708
+ const { RedoclyOAuthClient: RedoclyOAuthClient2 } = await import("./chunks/N2TQJ4KM.js");
12703
12709
  const oauthClient = new RedoclyOAuthClient2();
12704
12710
  const reuniteUrl = getReuniteUrl(config3, args.residency);
12705
12711
  const logged_in = await oauthClient.isAuthorized(reuniteUrl);
@@ -12747,7 +12753,7 @@ async function sendTelemetry({
12747
12753
  category: "product",
12748
12754
  data: eventData
12749
12755
  });
12750
- const { otelTelemetry } = await import("./chunks/7PWB3SB5.js");
12756
+ const { otelTelemetry } = await import("./chunks/G76UYYPW.js");
12751
12757
  otelTelemetry.send(cloudEvent);
12752
12758
  } catch (err) {
12753
12759
  }
@@ -13577,7 +13583,7 @@ yargs_default(hideBin(process.argv)).version("version", "Show version number.",
13577
13583
  return true;
13578
13584
  }),
13579
13585
  async (argv) => {
13580
- const { handlerBuildCommand } = await import("./chunks/XTSKVMID.js");
13586
+ const { handlerBuildCommand } = await import("./chunks/OPV3WWWU.js");
13581
13587
  commandWrapper(handlerBuildCommand)(argv);
13582
13588
  }
13583
13589
  ).command(
@@ -13775,6 +13781,144 @@ yargs_default(hideBin(process.argv)).version("version", "Show version number.",
13775
13781
  async (argv) => {
13776
13782
  commandWrapper(handleScorecardClassic)(argv);
13777
13783
  }
13784
+ ).command(
13785
+ "drift <traffic>",
13786
+ "Detect drift between recorded HTTP traffic and an OpenAPI description [experimental].",
13787
+ (yargs) => yargs.env("REDOCLY_CLI_DRIFT").positional("traffic", {
13788
+ describe: "Path to a traffic log file or folder (HAR, Kong, Nginx/Apache JSON, NDJSON).",
13789
+ type: "string"
13790
+ }).option({
13791
+ api: {
13792
+ describe: "OpenAPI description file or folder to validate against.",
13793
+ type: "string",
13794
+ demandOption: true
13795
+ },
13796
+ "traffic-format": {
13797
+ describe: "Traffic input format.",
13798
+ choices: [
13799
+ "auto",
13800
+ "har",
13801
+ "kong",
13802
+ "nginx-json",
13803
+ "apache-json",
13804
+ "ndjson"
13805
+ ],
13806
+ default: "auto"
13807
+ },
13808
+ "report-format": {
13809
+ describe: "Output format.",
13810
+ alias: "format",
13811
+ choices: ["pretty", "json", "csv", "sarif"],
13812
+ default: "pretty"
13813
+ },
13814
+ "match-mode": {
13815
+ describe: "Endpoint matching mode (how requests are located via the description servers). Mutually exclusive with --server.",
13816
+ choices: ["strict-host", "basepath"],
13817
+ defaultDescription: "strict-host"
13818
+ },
13819
+ "ignore-cookies": {
13820
+ describe: "Ignore cookie-based checks (useful for logs exported without cookies).",
13821
+ type: "boolean",
13822
+ default: false
13823
+ },
13824
+ "max-findings": {
13825
+ describe: "Maximum findings shown in pretty output.",
13826
+ type: "number",
13827
+ default: 10
13828
+ },
13829
+ "min-severity": {
13830
+ describe: "Discard findings below this severity from the report.",
13831
+ choices: ["info", "warning", "error"],
13832
+ default: "info"
13833
+ },
13834
+ rules: {
13835
+ describe: "Comma-separated builtin rules to run.",
13836
+ type: "string"
13837
+ },
13838
+ output: {
13839
+ alias: "o",
13840
+ describe: "Write the drift report (in the format selected with --format) to this file instead of stdout.",
13841
+ type: "string"
13842
+ },
13843
+ server: {
13844
+ describe: "Server URL the traffic was captured against: only requests under it are considered, and the rest of their URL is treated as the API path. It replaces the description servers and the remaining path is matched against the description paths directly. Mutually exclusive with --match-mode.",
13845
+ type: "string"
13846
+ },
13847
+ config: { describe: "Path to the config file.", type: "string" },
13848
+ "lint-config": {
13849
+ describe: "Severity level for config file linting.",
13850
+ choices: ["warn", "error", "off"],
13851
+ default: "warn"
13852
+ }
13853
+ }),
13854
+ async (argv) => {
13855
+ const { handleDrift } = await import("./chunks/U2FUDUNT.js");
13856
+ commandWrapper(handleDrift)(argv);
13857
+ }
13858
+ ).command(
13859
+ "proxy",
13860
+ "Capture live HTTP traffic through a reverse proxy into a HAR file, optionally validating it against an OpenAPI description [experimental].",
13861
+ (yargs) => yargs.env("REDOCLY_CLI_PROXY").option({
13862
+ target: {
13863
+ describe: "Upstream base URL to forward captured requests to.",
13864
+ type: "string",
13865
+ demandOption: true
13866
+ },
13867
+ port: {
13868
+ describe: "Port the proxy listens on.",
13869
+ type: "number",
13870
+ default: 4040
13871
+ },
13872
+ host: {
13873
+ describe: "Host the proxy binds to.",
13874
+ type: "string",
13875
+ default: "127.0.0.1"
13876
+ },
13877
+ har: {
13878
+ describe: "Path to the HAR file where captured traffic is written.",
13879
+ type: "string",
13880
+ demandOption: true
13881
+ },
13882
+ api: {
13883
+ describe: "OpenAPI description file or folder to validate captured traffic against (live).",
13884
+ type: "string"
13885
+ },
13886
+ "report-format": {
13887
+ describe: "Output format for the validation report printed on shutdown.",
13888
+ alias: "format",
13889
+ choices: ["pretty", "json", "csv", "sarif"],
13890
+ default: "pretty"
13891
+ },
13892
+ "match-mode": {
13893
+ describe: "Endpoint matching mode.",
13894
+ choices: ["strict-host", "basepath"],
13895
+ default: "strict-host"
13896
+ },
13897
+ "ignore-cookies": {
13898
+ describe: "Ignore cookie-based checks.",
13899
+ type: "boolean",
13900
+ default: false
13901
+ },
13902
+ "max-findings": {
13903
+ describe: "Maximum findings shown in pretty output.",
13904
+ type: "number",
13905
+ default: 10
13906
+ },
13907
+ rules: {
13908
+ describe: "Comma-separated builtin rules to run.",
13909
+ type: "string"
13910
+ },
13911
+ config: { describe: "Path to the config file.", type: "string" },
13912
+ "lint-config": {
13913
+ describe: "Severity level for config file linting.",
13914
+ choices: ["warn", "error", "off"],
13915
+ default: "warn"
13916
+ }
13917
+ }),
13918
+ async (argv) => {
13919
+ const { handleProxy } = await import("./chunks/NPHT727D.js");
13920
+ commandWrapper(handleProxy)(argv);
13921
+ }
13778
13922
  ).completion("completion", "Generate autocomplete script for `redocly` command.").demandCommand(1).middleware([notifyUpdateCliVersion]).strict().argv;
13779
13923
  /*! Bundled license information:
13780
13924
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/cli",
3
- "version": "2.36.0",
3
+ "version": "2.38.0",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "type": "module",