@redocly/cli 2.39.0 → 2.41.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,492 @@
1
+ import { createRequire as __createRequire } from 'node:module';
2
+ const require = __createRequire(import.meta.url);
3
+ import {
4
+ isPlainObject
5
+ } from "./YK6T7IHG.js";
6
+
7
+ // src/commands/drift/utils/files.ts
8
+ import { open, readdir, stat } from "node:fs/promises";
9
+ import path from "node:path";
10
+ var SPEC_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".yaml", ".yml", ".json"]);
11
+ async function listOpenApiFiles(rootDir) {
12
+ const output = [];
13
+ async function walk(currentDir) {
14
+ const entries = await readdir(currentDir, { withFileTypes: true });
15
+ for (const entry of entries) {
16
+ if (entry.name.startsWith(".")) {
17
+ continue;
18
+ }
19
+ const absolutePath = path.join(currentDir, entry.name);
20
+ if (entry.isDirectory()) {
21
+ await walk(absolutePath);
22
+ continue;
23
+ }
24
+ if (entry.isFile() && SPEC_FILE_EXTENSIONS.has(path.extname(entry.name).toLowerCase())) {
25
+ output.push(absolutePath);
26
+ }
27
+ }
28
+ }
29
+ await walk(rootDir);
30
+ output.sort();
31
+ return output;
32
+ }
33
+ async function readProbe(filePath, maxBytes = 4096) {
34
+ const fileHandle = await open(filePath, "r");
35
+ try {
36
+ const buffer = Buffer.allocUnsafe(maxBytes);
37
+ const { bytesRead } = await fileHandle.read(buffer, 0, maxBytes, 0);
38
+ return buffer.toString("utf8", 0, bytesRead);
39
+ } finally {
40
+ await fileHandle.close();
41
+ }
42
+ }
43
+ function normalizeFsPath(value) {
44
+ return path.resolve(process.cwd(), value);
45
+ }
46
+ async function listFilesRecursively(rootPath) {
47
+ const stats = await stat(rootPath);
48
+ if (stats.isFile()) {
49
+ return [rootPath];
50
+ }
51
+ if (!stats.isDirectory()) {
52
+ return [];
53
+ }
54
+ const output = [];
55
+ async function walk(currentDir) {
56
+ const entries = await readdir(currentDir, { withFileTypes: true });
57
+ for (const entry of entries) {
58
+ if (entry.name.startsWith(".")) {
59
+ continue;
60
+ }
61
+ const absolutePath = path.join(currentDir, entry.name);
62
+ if (entry.isDirectory()) {
63
+ await walk(absolutePath);
64
+ continue;
65
+ }
66
+ if (entry.isFile()) {
67
+ output.push(absolutePath);
68
+ }
69
+ }
70
+ }
71
+ await walk(rootPath);
72
+ output.sort();
73
+ return output;
74
+ }
75
+
76
+ // src/commands/drift/utils/http.ts
77
+ var DUMMY_HOST = "drift.local";
78
+ var DUMMY_BASE_URL = `http://${DUMMY_HOST}`;
79
+ function isSyntheticHost(host) {
80
+ return host === DUMMY_HOST;
81
+ }
82
+ var IGNORED_UNDOCUMENTED_HEADERS = /* @__PURE__ */ new Set([
83
+ "accept",
84
+ "accept-charset",
85
+ "accept-encoding",
86
+ "accept-language",
87
+ "authorization",
88
+ "baggage",
89
+ "cache-control",
90
+ "cdn-loop",
91
+ "cookie",
92
+ "connection",
93
+ "content-length",
94
+ "content-type",
95
+ "dpr",
96
+ "dnt",
97
+ "downlink",
98
+ "ect",
99
+ "forwarded",
100
+ "host",
101
+ "if-match",
102
+ "if-modified-since",
103
+ "if-none-match",
104
+ "if-range",
105
+ "if-unmodified-since",
106
+ "origin",
107
+ "pragma",
108
+ "priority",
109
+ "range",
110
+ "referer",
111
+ "sec-fetch-dest",
112
+ "sec-fetch-mode",
113
+ "sec-fetch-site",
114
+ "sec-fetch-user",
115
+ "sec-gpc",
116
+ "sentry-trace",
117
+ "te",
118
+ "traceparent",
119
+ "tracestate",
120
+ "upgrade",
121
+ "upgrade-insecure-requests",
122
+ "user-agent",
123
+ "via",
124
+ "x-amzn-trace-id",
125
+ "x-client-trace-id",
126
+ "x-cloud-trace-context",
127
+ "x-correlation-id",
128
+ "x-http-method-override",
129
+ "x-method-override",
130
+ "x-real-ip",
131
+ "x-request-id",
132
+ "x-forwarded-for",
133
+ "x-forwarded-host",
134
+ "x-forwarded-port",
135
+ "x-forwarded-proto"
136
+ ]);
137
+ var IGNORED_UNDOCUMENTED_HEADER_PREFIXES = [
138
+ "cf-",
139
+ "sec-ch-",
140
+ "sec-fetch-",
141
+ "x-b3-",
142
+ "x-envoy-",
143
+ "x-forwarded-"
144
+ ];
145
+ var SET_COOKIE_SEPARATOR = "\n";
146
+ function splitSetCookieHeader(value) {
147
+ return value.split(SET_COOKIE_SEPARATOR);
148
+ }
149
+ function appendHeaderValue(result, name, value) {
150
+ const key = name.toLowerCase();
151
+ const existing = result[key];
152
+ if (existing === void 0) {
153
+ result[key] = value;
154
+ return;
155
+ }
156
+ result[key] = key === "set-cookie" ? `${existing}${SET_COOKIE_SEPARATOR}${value}` : `${existing},${value}`;
157
+ }
158
+ function normalizeHeaders(input) {
159
+ if (!isPlainObject(input) && !Array.isArray(input)) {
160
+ return {};
161
+ }
162
+ const result = {};
163
+ if (Array.isArray(input)) {
164
+ for (const item of input) {
165
+ if (!isPlainObject(item)) {
166
+ continue;
167
+ }
168
+ const { name, value } = item;
169
+ if (typeof name === "string" && value !== void 0) {
170
+ appendHeaderValue(result, name, String(value));
171
+ }
172
+ }
173
+ return result;
174
+ }
175
+ for (const [key, value] of Object.entries(input)) {
176
+ if (value === void 0 || value === null) {
177
+ continue;
178
+ }
179
+ if (Array.isArray(value)) {
180
+ for (const item of value) {
181
+ appendHeaderValue(result, key, String(item));
182
+ }
183
+ continue;
184
+ }
185
+ appendHeaderValue(result, key, String(value));
186
+ }
187
+ return result;
188
+ }
189
+ function parseUrl(input) {
190
+ try {
191
+ return new URL(input);
192
+ } catch {
193
+ return new URL(input, DUMMY_BASE_URL);
194
+ }
195
+ }
196
+ function getPathWithoutTrailingSlash(pathname) {
197
+ if (pathname.length > 1 && pathname.endsWith("/")) {
198
+ return pathname.slice(0, -1);
199
+ }
200
+ return pathname;
201
+ }
202
+ function escapeRegex(value) {
203
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
204
+ }
205
+ function compileOpenApiPath(pathTemplate) {
206
+ const params = [];
207
+ let score = 0;
208
+ const absolutePath = pathTemplate.startsWith("/") ? pathTemplate : `/${pathTemplate}`;
209
+ const normalizedPath = getPathWithoutTrailingSlash(absolutePath);
210
+ const regexBody = normalizedPath.split("/").map((segment) => {
211
+ if (!segment) {
212
+ return "";
213
+ }
214
+ const paramMatch = segment.match(/^\{([^}]+)\}$/);
215
+ if (paramMatch) {
216
+ params.push(paramMatch[1]);
217
+ return "([^/]+)";
218
+ }
219
+ score += 2;
220
+ return escapeRegex(segment);
221
+ }).join("/");
222
+ return {
223
+ regex: new RegExp(`^${regexBody || "/"}$`),
224
+ params,
225
+ score
226
+ };
227
+ }
228
+ function parseJsonBodyIfPresent(contentType, bodyText) {
229
+ if (!bodyText) {
230
+ return void 0;
231
+ }
232
+ if (!isJsonMime(contentType)) {
233
+ return void 0;
234
+ }
235
+ try {
236
+ return JSON.parse(bodyText);
237
+ } catch {
238
+ return void 0;
239
+ }
240
+ }
241
+ function normalizeContentType(contentType) {
242
+ if (!contentType) {
243
+ return "";
244
+ }
245
+ return contentType.split(";")[0]?.trim().toLowerCase() ?? "";
246
+ }
247
+ function isJsonMime(contentType) {
248
+ const mime = normalizeContentType(contentType);
249
+ return mime === "application/json" || mime.endsWith("+json");
250
+ }
251
+ function pickSchemaByMime(contentMap, contentType) {
252
+ const requestedMime = normalizeContentType(contentType);
253
+ if (!requestedMime) {
254
+ return contentMap["application/json"] ?? contentMap["*/*"];
255
+ }
256
+ if (contentMap[requestedMime] !== void 0) {
257
+ return contentMap[requestedMime];
258
+ }
259
+ const [type, subtype] = requestedMime.split("/");
260
+ if (type && subtype) {
261
+ const wildcardSubtype = `${type}/*`;
262
+ if (contentMap[wildcardSubtype] !== void 0) {
263
+ return contentMap[wildcardSubtype];
264
+ }
265
+ }
266
+ return contentMap["*/*"];
267
+ }
268
+ function parseHeaderIgnoreList(patterns) {
269
+ const names = /* @__PURE__ */ new Set();
270
+ const prefixes = [];
271
+ for (const pattern of patterns) {
272
+ const normalized = pattern.trim().toLowerCase();
273
+ if (normalized.length === 0) {
274
+ continue;
275
+ }
276
+ if (normalized.endsWith("*")) {
277
+ prefixes.push(normalized.slice(0, -1));
278
+ } else {
279
+ names.add(normalized);
280
+ }
281
+ }
282
+ return { names, prefixes };
283
+ }
284
+ function shouldIgnoreHeaderAsUndocumented(headerName, extraIgnored) {
285
+ const normalizedHeaderName = headerName.toLowerCase();
286
+ if (normalizedHeaderName.startsWith(":")) {
287
+ return true;
288
+ }
289
+ if (IGNORED_UNDOCUMENTED_HEADERS.has(normalizedHeaderName) || IGNORED_UNDOCUMENTED_HEADER_PREFIXES.some((prefix) => normalizedHeaderName.startsWith(prefix))) {
290
+ return true;
291
+ }
292
+ if (extraIgnored) {
293
+ return extraIgnored.names.has(normalizedHeaderName) || extraIgnored.prefixes.some((prefix) => normalizedHeaderName.startsWith(prefix));
294
+ }
295
+ return false;
296
+ }
297
+
298
+ // src/commands/drift/log-formats/helpers.ts
299
+ import { createReadStream } from "node:fs";
300
+ import { readFile } from "node:fs/promises";
301
+ import { createInterface } from "node:readline";
302
+ function coerceString(value) {
303
+ if (value === void 0 || value === null) {
304
+ return void 0;
305
+ }
306
+ if (typeof value === "string") {
307
+ return value;
308
+ }
309
+ if (Buffer.isBuffer(value)) {
310
+ return value.toString("utf8");
311
+ }
312
+ if (isPlainObject(value) || Array.isArray(value)) {
313
+ try {
314
+ return JSON.stringify(value);
315
+ } catch {
316
+ return String(value);
317
+ }
318
+ }
319
+ return String(value);
320
+ }
321
+ function coerceNumber(value) {
322
+ if (typeof value === "number" && Number.isFinite(value)) {
323
+ return value;
324
+ }
325
+ if (typeof value === "string" && value.trim() !== "") {
326
+ const parsed = Number(value);
327
+ if (Number.isFinite(parsed)) {
328
+ return parsed;
329
+ }
330
+ }
331
+ return void 0;
332
+ }
333
+ function decodeBody(value, encoding) {
334
+ if (value === void 0 || value === null) {
335
+ return void 0;
336
+ }
337
+ if (encoding === "base64" && typeof value === "string") {
338
+ try {
339
+ return Buffer.from(value, "base64").toString("utf8");
340
+ } catch {
341
+ return value;
342
+ }
343
+ }
344
+ return coerceString(value);
345
+ }
346
+ function createNormalizedExchange(seed, index, source) {
347
+ const method = seed.method?.toUpperCase();
348
+ const url = seed.url;
349
+ if (!method || !url) {
350
+ return null;
351
+ }
352
+ const requestHeaders = normalizeHeaders(seed.requestHeaders);
353
+ let parsedUrl = parseUrl(url);
354
+ if (isSyntheticHost(parsedUrl.host) && requestHeaders.host) {
355
+ try {
356
+ parsedUrl = new URL(
357
+ `${parsedUrl.protocol}//${requestHeaders.host}${parsedUrl.pathname}${parsedUrl.search}`
358
+ );
359
+ } catch {
360
+ parsedUrl = parseUrl(url);
361
+ }
362
+ }
363
+ const requestContentType = seed.requestContentType ?? requestHeaders["content-type"];
364
+ const requestBodyText = decodeBody(seed.requestBody);
365
+ const request = {
366
+ method,
367
+ url: parsedUrl.toString(),
368
+ path: parsedUrl.pathname,
369
+ query: parsedUrl.searchParams,
370
+ protocol: parsedUrl.protocol,
371
+ protocolKnown: seed.schemeKnown ?? /^https?:\/\//i.test(url),
372
+ host: isSyntheticHost(parsedUrl.host) ? void 0 : parsedUrl.host || void 0,
373
+ headers: requestHeaders,
374
+ contentType: requestContentType,
375
+ bodyText: requestBodyText,
376
+ bodyJson: parseJsonBodyIfPresent(requestContentType, requestBodyText)
377
+ };
378
+ let response;
379
+ const responseStatus = seed.responseStatus;
380
+ if (responseStatus !== void 0) {
381
+ const responseHeaders = normalizeHeaders(seed.responseHeaders);
382
+ const responseContentType = seed.responseContentType ?? responseHeaders["content-type"];
383
+ const responseBodyText = decodeBody(seed.responseBody);
384
+ response = {
385
+ status: responseStatus,
386
+ statusText: seed.responseStatusText,
387
+ headers: responseHeaders,
388
+ contentType: responseContentType,
389
+ bodyText: responseBodyText,
390
+ bodyJson: parseJsonBodyIfPresent(responseContentType, responseBodyText)
391
+ };
392
+ }
393
+ return {
394
+ index,
395
+ source,
396
+ startedAt: seed.startedAt,
397
+ request,
398
+ response,
399
+ raw: seed.raw
400
+ };
401
+ }
402
+ async function* streamNdjsonObjects(filePath) {
403
+ const readStream = createReadStream(filePath, { encoding: "utf8" });
404
+ const reader = createInterface({ input: readStream, crlfDelay: Infinity });
405
+ for await (const line of reader) {
406
+ const trimmed = line.trim();
407
+ if (!trimmed) {
408
+ continue;
409
+ }
410
+ try {
411
+ const parsed = JSON.parse(trimmed);
412
+ if (isPlainObject(parsed)) {
413
+ yield parsed;
414
+ }
415
+ } catch {
416
+ }
417
+ }
418
+ }
419
+ async function* iterateJsonArray(filePath, arrayPath) {
420
+ const content = await readFile(filePath, "utf8");
421
+ let value = JSON.parse(content);
422
+ if (arrayPath) {
423
+ for (const key of arrayPath.split(".")) {
424
+ value = isPlainObject(value) ? value[key] : void 0;
425
+ }
426
+ }
427
+ if (Array.isArray(value)) {
428
+ for (const item of value) {
429
+ if (isPlainObject(item)) {
430
+ yield item;
431
+ }
432
+ }
433
+ }
434
+ }
435
+ function pickHeaderContentType(headers) {
436
+ const normalized = normalizeHeaders(headers);
437
+ return normalized["content-type"];
438
+ }
439
+
440
+ // src/commands/drift/utils/server.ts
441
+ function normalizeServerPrefix(server) {
442
+ const trimmed = server?.replace(/\/+$/, "");
443
+ return trimmed || void 0;
444
+ }
445
+ function stripPrefixFromPath(pathname, prefixPath) {
446
+ if (!prefixPath || prefixPath === "/") {
447
+ return pathname || "/";
448
+ }
449
+ if (pathname === prefixPath) {
450
+ return "/";
451
+ }
452
+ if (!pathname.startsWith(`${prefixPath}/`)) {
453
+ return void 0;
454
+ }
455
+ return pathname.slice(prefixPath.length) || "/";
456
+ }
457
+ function resolvePathForServer(request, server) {
458
+ if (server.startsWith("/")) {
459
+ return stripPrefixFromPath(request.path, server);
460
+ }
461
+ const serverUrl = parseUrl(server.includes("://") ? server : `http://${server}`);
462
+ if (isSyntheticHost(serverUrl.host) || request.host !== void 0 && request.host.toLowerCase() !== serverUrl.host) {
463
+ return void 0;
464
+ }
465
+ return stripPrefixFromPath(request.path, getPathWithoutTrailingSlash(serverUrl.pathname));
466
+ }
467
+
468
+ export {
469
+ listOpenApiFiles,
470
+ readProbe,
471
+ normalizeFsPath,
472
+ listFilesRecursively,
473
+ isSyntheticHost,
474
+ splitSetCookieHeader,
475
+ parseUrl,
476
+ getPathWithoutTrailingSlash,
477
+ compileOpenApiPath,
478
+ normalizeContentType,
479
+ isJsonMime,
480
+ pickSchemaByMime,
481
+ parseHeaderIgnoreList,
482
+ shouldIgnoreHeaderAsUndocumented,
483
+ coerceString,
484
+ coerceNumber,
485
+ decodeBody,
486
+ createNormalizedExchange,
487
+ streamNdjsonObjects,
488
+ iterateJsonArray,
489
+ pickHeaderContentType,
490
+ normalizeServerPrefix,
491
+ resolvePathForServer
492
+ };
@@ -1,36 +1,19 @@
1
1
  import { createRequire as __createRequire } from 'node:module';
2
2
  const require = __createRequire(import.meta.url);
3
3
  import {
4
- ValidationSession,
5
4
  coerceNumber,
6
5
  coerceString,
7
6
  createNormalizedExchange,
8
7
  decodeBody,
9
8
  iterateJsonArray,
10
- listFilesRecursively,
11
- loadOpenApiIndex,
12
9
  normalizeContentType,
13
- normalizeFsPath,
14
- parseCsv,
15
10
  pickHeaderContentType,
16
11
  readProbe,
17
- renderReport,
18
12
  streamNdjsonObjects
19
- } from "./WST4KAJO.js";
13
+ } from "./ER45DAHG.js";
20
14
  import {
21
- AbortFlowError,
22
- exitWithError
23
- } from "./U4V3W7MN.js";
24
- import {
25
- isPlainObject,
26
- logger
27
- } from "./KYZEVOA2.js";
28
- import "./Z2I5YXYN.js";
29
- import "./5ILQMFXK.js";
30
-
31
- // src/commands/drift/index.ts
32
- import { mkdir, writeFile } from "node:fs/promises";
33
- import path5 from "node:path";
15
+ isPlainObject
16
+ } from "./YK6T7IHG.js";
34
17
 
35
18
  // src/commands/drift/log-formats/har.ts
36
19
  import path from "node:path";
@@ -203,15 +186,15 @@ function buildUrl(record, request) {
203
186
  return { url: directUrl };
204
187
  }
205
188
  const host = coerceString(request?.host ?? request?.headers?.host ?? record?.host);
206
- const path6 = coerceString(request?.path ?? record?.path ?? request?.pathname);
189
+ const path5 = coerceString(request?.path ?? record?.path ?? request?.pathname);
207
190
  const explicitScheme = coerceString(request?.scheme ?? record?.scheme);
208
- if (host && path6) {
191
+ if (host && path5) {
209
192
  return {
210
- url: `${explicitScheme ?? "http"}://${host}${path6}`,
193
+ url: `${explicitScheme ?? "http"}://${host}${path5}`,
211
194
  schemeKnown: Boolean(explicitScheme)
212
195
  };
213
196
  }
214
- return { url: path6, schemeKnown: Boolean(explicitScheme) };
197
+ return { url: path5, schemeKnown: Boolean(explicitScheme) };
215
198
  }
216
199
  function normalizeGenericRecord(record, index, source) {
217
200
  const request = getRequestCandidate(record);
@@ -426,146 +409,6 @@ async function selectTrafficParser(filePath, format) {
426
409
  return PARSERS.find((candidate) => candidate.canParse(filePath, probe));
427
410
  }
428
411
 
429
- // src/commands/drift/engine/runner.ts
430
- async function runTrafficValidation(options) {
431
- const trafficFiles = await listFilesRecursively(options.trafficPath);
432
- if (trafficFiles.length === 0) {
433
- throw new Error("No traffic files found in the provided traffic path.");
434
- }
435
- const session = ValidationSession.create({
436
- openApiIndex: options.openApiIndex,
437
- matchMode: options.matchMode,
438
- ignoreCookies: options.ignoreCookies,
439
- previewFindingsLimit: options.previewFindingsLimit,
440
- activeRules: options.activeRules,
441
- server: options.server,
442
- minSeverity: options.minSeverity
443
- });
444
- let supportedTrafficFileCount = 0;
445
- let exchangeIndex = 0;
446
- for (const trafficFile of trafficFiles) {
447
- const parser = await selectTrafficParser(trafficFile, options.format);
448
- if (!parser) {
449
- logger.warn(`Skipping traffic file with unrecognized format: ${trafficFile}
450
- `);
451
- continue;
452
- }
453
- supportedTrafficFileCount += 1;
454
- for await (const exchange of parser.parse(trafficFile)) {
455
- await session.process({ ...exchange, index: exchangeIndex });
456
- exchangeIndex += 1;
457
- }
458
- }
459
- if (supportedTrafficFileCount === 0) {
460
- throw new Error(
461
- "No supported traffic files found. In auto mode, files must match built-in traffic parser signatures."
462
- );
463
- }
464
- if (exchangeIndex === 0) {
465
- throw new Error("No HTTP exchanges were parsed from the provided traffic files.");
466
- }
467
- return session.finalize();
468
- }
469
-
470
- // src/commands/drift/index.ts
471
- var USE_COLOR = Boolean(process.stdout.isTTY) && process.env.NO_COLOR === void 0;
472
- function collectSpecServerUrls(openApiIndex) {
473
- const urls = /* @__PURE__ */ new Set();
474
- for (const operations of openApiIndex.operationsByMethod.values()) {
475
- for (const operation of operations) {
476
- for (const server of operation.servers) {
477
- urls.add(server.rawUrl);
478
- }
479
- }
480
- }
481
- return Array.from(urls).sort();
482
- }
483
- function warnWhenNothingMatched(summary, openApiIndex, server) {
484
- const validatedExchanges = summary.totalExchanges - summary.skippedExchanges;
485
- if (validatedExchanges === 0 && summary.skippedExchanges > 0) {
486
- logger.warn(
487
- `All ${summary.skippedExchanges} exchange(s) were outside the --server "${server}" and were skipped. Check that the server matches the traffic URLs.
488
- `
489
- );
490
- return;
491
- }
492
- if (summary.documentedExchanges > 0 || validatedExchanges === 0) {
493
- return;
494
- }
495
- const serverUrls = collectSpecServerUrls(openApiIndex);
496
- const hint = server ? `Check that the --server "${server}" matches the traffic URLs and that the description paths align with the remainder.` : summary.hostCompatibleExchanges === validatedExchanges ? `The traffic hosts are compatible with the description servers (${serverUrls.join(
497
- ", "
498
- )}), so the endpoints are likely undocumented; if they should be documented, check that the description base paths and paths align with the traffic URLs, or use --server to declare the server the traffic was captured against.` : `Check that the traffic host and base path match the description servers (${serverUrls.join(
499
- ", "
500
- )}), or use --server to declare the server the traffic was captured against.`;
501
- logger.warn(
502
- `None of the ${validatedExchanges} validated exchange(s) matched a documented operation. ${hint}
503
- `
504
- );
505
- }
506
- async function writeOutput(outputPath, content) {
507
- const resolved = normalizeFsPath(outputPath);
508
- await mkdir(path5.dirname(resolved), { recursive: true });
509
- await writeFile(resolved, content, "utf8");
510
- }
511
- async function handleDrift({ argv, config }) {
512
- const trafficPath = normalizeFsPath(argv.traffic);
513
- const trafficFormat = argv["traffic-format"];
514
- const activeRules = argv.rules ? parseCsv(argv.rules) : void 0;
515
- const server = argv.server;
516
- if (server && argv["match-mode"]) {
517
- return exitWithError(
518
- "The --server and --match-mode options are mutually exclusive: --match-mode controls how requests are located via the description servers, while --server replaces the description servers with the one the traffic was captured against."
519
- );
520
- }
521
- const matchMode = argv["match-mode"] ?? "strict-host";
522
- const specPath = normalizeFsPath(argv.api);
523
- const openApiIndex = await loadOpenApiIndex(specPath, config);
524
- if (openApiIndex.loadedOperations === 0) {
525
- return exitWithError(`No OpenAPI operations were loaded from: ${specPath}`);
526
- }
527
- const { runId, summary, findings } = await runTrafficValidation({
528
- trafficPath,
529
- format: trafficFormat,
530
- matchMode,
531
- ignoreCookies: argv["ignore-cookies"],
532
- previewFindingsLimit: argv["max-findings"],
533
- activeRules,
534
- openApiIndex,
535
- server,
536
- minSeverity: argv["min-severity"]
537
- });
538
- warnWhenNothingMatched(summary, openApiIndex, server);
539
- const report = renderReport(
540
- {
541
- runId,
542
- summary,
543
- findings,
544
- meta: {
545
- specSource: specPath,
546
- trafficPath,
547
- format: trafficFormat,
548
- matchMode,
549
- server
550
- }
551
- },
552
- {
553
- format: argv["report-format"],
554
- color: USE_COLOR && argv["report-format"] === "pretty" && !argv.output,
555
- maxFindings: argv["max-findings"]
556
- }
557
- );
558
- if (argv.output) {
559
- await writeOutput(argv.output, report);
560
- logger.info(`Drift report written to: ${normalizeFsPath(argv.output)}
561
- `);
562
- } else {
563
- logger.output(report);
564
- }
565
- if (summary.findingsBySeverity.error > 0) {
566
- throw new AbortFlowError("Drift detected.");
567
- }
568
- }
569
412
  export {
570
- handleDrift
413
+ selectTrafficParser
571
414
  };