contract-drift-detection 0.1.4 → 0.1.6

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/CHANGELOG.md CHANGED
@@ -2,6 +2,32 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## 0.1.6 - 2026-03-16
6
+
7
+ ### Improved
8
+
9
+ - Drift detection alerts now render as clean, colorized multiline terminal output instead of JSON-escaped logger lines.
10
+ - Preserved structured drift payload logging at debug level for observability without cluttering normal terminal UX.
11
+ - Updated drift proxy test assertions to validate pretty stderr output behavior.
12
+
13
+ ## 0.1.5 - 2026-03-16
14
+
15
+ ### Added
16
+
17
+ - New drift reporting outputs via `--reporter pretty|json|junit`.
18
+ - Optional report destination via `--report-file <path>`.
19
+ - Strict drift mode via `--strict` to enforce `additionalProperties: false` behavior during validation.
20
+ - Path-based drift ignore controls via repeated `--drift-ignore <path>` and `.driftignore` support.
21
+ - New diagnostics endpoint: `GET /__drift` for in-memory drift issue visibility.
22
+ - New unit test coverage for drift analyzer strict/ignore behavior.
23
+ - New CLI e2e coverage for discover failure UX.
24
+
25
+ ### Improved
26
+
27
+ - CI defaults now support fail-fast drift behavior with `--fail-on-drift`.
28
+ - Drift validation pipeline now supports machine-readable reporting for CI gates.
29
+ - Server-level drift callback flow for automated fail/report handling.
30
+
5
31
  ## 0.1.4 - 2026-03-16
6
32
 
7
33
  ### Improved
package/dist/cli.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import process from "process";
4
+ import process2 from "process";
5
5
 
6
6
  // src/config.ts
7
- import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
7
+ import { mkdir as mkdir2, readFile, writeFile as writeFile2 } from "fs/promises";
8
8
  import path2 from "path";
9
9
  import { Command } from "commander";
10
10
 
@@ -238,11 +238,26 @@ function resolvePathFromCwd(cwd, value) {
238
238
  return path2.isAbsolute(value) ? value : path2.join(cwd, value);
239
239
  }
240
240
  function applyServeOptions(command) {
241
- return command.option("--spec <path>", "Path to an OpenAPI 3.x file").option("--spec-url <url>", "Remote URL to an OpenAPI 3.x file").option("--discover <backend-url>", "Discover OpenAPI from a backend URL and cache it locally").option("--port <port>", "Port to bind the server", "4010").option("--host <host>", "Host to bind the server", "0.0.0.0").option("--db <path>", "JSON database path", ".mock-db.json").option("--cors-origin <origin>", "CORS origin (default is *)", "*").option("--drift-check <url>", "Forward traffic to a real backend and validate responses").option("--fallback-to-mock", "Fallback to mock responses when proxying fails", false).option("--verbose", "Enable verbose logging", false);
241
+ return command.option("--spec <path>", "Path to an OpenAPI 3.x file").option("--spec-url <url>", "Remote URL to an OpenAPI 3.x file").option("--discover <backend-url>", "Discover OpenAPI from a backend URL and cache it locally").option("--port <port>", "Port to bind the server", "4010").option("--host <host>", "Host to bind the server", "0.0.0.0").option("--db <path>", "JSON database path", ".mock-db.json").option("--cors-origin <origin>", "CORS origin (default is *)", "*").option("--drift-check <url>", "Forward traffic to a real backend and validate responses").option("--strict", "Treat additional fields as drift", false).option("--drift-ignore <paths>", "Comma-separated JSON pointer paths to ignore during drift checks").option("--reporter <type>", "Drift reporter: pretty|json|junit", "pretty").option("--report-file <path>", "Output file path for json/junit reporters").option("--fail-on-drift", "Exit with code 1 when drift is detected (enabled by default in CI)", false).option("--fallback-to-mock", "Fallback to mock responses when proxying fails", false).option("--verbose", "Enable verbose logging", false);
242
+ }
243
+ function parseCsvOption(value) {
244
+ if (!value || typeof value !== "string") {
245
+ return [];
246
+ }
247
+ return value.split(",").map((entry) => entry.trim()).filter(Boolean);
248
+ }
249
+ async function loadDriftIgnoreFromFile(cwd) {
250
+ const ignoreFilePath = path2.join(cwd, ".driftignore");
251
+ try {
252
+ const raw = await readFile(ignoreFilePath, "utf8");
253
+ return raw.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
254
+ } catch {
255
+ return [];
256
+ }
242
257
  }
243
258
  function createCli() {
244
259
  const program = new Command();
245
- program.name("contract-drift-detection").description("Stateful OpenAPI mock server with contract drift detection").version("0.1.0");
260
+ program.name("contract-drift-detection").description("Stateful OpenAPI mock server with contract drift detection").version("0.1.6");
246
261
  applyServeOptions(program);
247
262
  applyServeOptions(program.command("serve").description("Start the mock engine"));
248
263
  program.command("init").description("Create a starter config for the current workspace").option("--spec <path>", "Default OpenAPI path", "openapi.yaml").option("--template <name>", "Template to generate (rest-crud | none)", "rest-crud").option("--db <path>", "Default JSON database path", ".mock-db.json").option("--port <port>", "Default port", "4010").option("--host <host>", "Default host", "0.0.0.0");
@@ -262,6 +277,13 @@ async function resolveServeConfig(cwd, options) {
262
277
  if (!resolvedSpecPath) {
263
278
  throw new Error("Provide one of --spec, --spec-url, or --discover");
264
279
  }
280
+ const ignoreFromCli = parseCsvOption(options.driftIgnore);
281
+ const ignoreFromFile = await loadDriftIgnoreFromFile(cwd);
282
+ const mergedIgnore = Array.from(/* @__PURE__ */ new Set([...ignoreFromFile, ...ignoreFromCli]));
283
+ const reporter = String(options.reporter ?? "pretty");
284
+ if (!["pretty", "json", "junit"].includes(reporter)) {
285
+ throw new Error(`Invalid reporter '${reporter}'. Use one of: pretty, json, junit`);
286
+ }
265
287
  return {
266
288
  specPath: resolvedSpecPath,
267
289
  port: Number(options.port ?? 4010),
@@ -269,6 +291,11 @@ async function resolveServeConfig(cwd, options) {
269
291
  dbPath: resolvePathFromCwd(cwd, String(options.db ?? ".mock-db.json")),
270
292
  corsOrigin: String(options.corsOrigin ?? "*"),
271
293
  driftCheckTarget: options.driftCheck ? String(options.driftCheck) : void 0,
294
+ strictDrift: Boolean(options.strict),
295
+ driftIgnorePaths: mergedIgnore,
296
+ reporter,
297
+ reportFile: options.reportFile ? resolvePathFromCwd(cwd, String(options.reportFile)) : void 0,
298
+ failOnDrift: Boolean(options.failOnDrift) || process.env.CI === "true",
272
299
  fallbackToMockOnProxyError: Boolean(options.fallbackToMock),
273
300
  verbose: Boolean(options.verbose)
274
301
  };
@@ -298,7 +325,7 @@ async function writeStarterConfig(cwd, initConfig) {
298
325
  }
299
326
 
300
327
  // src/server.ts
301
- import path5 from "path";
328
+ import path6 from "path";
302
329
  import Fastify from "fastify";
303
330
  import cors from "@fastify/cors";
304
331
 
@@ -453,39 +480,112 @@ function applyDslMutation(extension, request, collection, defaultIdKey) {
453
480
  import Ajv from "ajv";
454
481
  import addFormats from "ajv-formats";
455
482
  import pc from "picocolors";
483
+ function deepClone2(value) {
484
+ return JSON.parse(JSON.stringify(value));
485
+ }
486
+ function pathMatchesIgnoreRule(instancePath, rule) {
487
+ if (!rule) {
488
+ return false;
489
+ }
490
+ const pathSegments = instancePath.split("/").filter(Boolean);
491
+ const ruleSegments = rule.split("/").filter(Boolean);
492
+ if (ruleSegments.length > pathSegments.length) {
493
+ return false;
494
+ }
495
+ return ruleSegments.every((segment, index) => segment === "*" || segment === pathSegments[index]);
496
+ }
497
+ function applyStrictAdditionalProperties(schema) {
498
+ const output = deepClone2(schema);
499
+ const walk = (candidate) => {
500
+ if (candidate.type === "object" || candidate.properties) {
501
+ if (candidate.additionalProperties === void 0) {
502
+ candidate.additionalProperties = false;
503
+ }
504
+ for (const value of Object.values(candidate.properties ?? {})) {
505
+ if (value && !("$ref" in value)) {
506
+ walk(value);
507
+ }
508
+ }
509
+ }
510
+ if (candidate.type === "array" && candidate.items && !("$ref" in candidate.items)) {
511
+ walk(candidate.items);
512
+ }
513
+ for (const entry of candidate.allOf ?? []) {
514
+ if (!("$ref" in entry)) {
515
+ walk(entry);
516
+ }
517
+ }
518
+ for (const entry of candidate.oneOf ?? []) {
519
+ if (!("$ref" in entry)) {
520
+ walk(entry);
521
+ }
522
+ }
523
+ for (const entry of candidate.anyOf ?? []) {
524
+ if (!("$ref" in entry)) {
525
+ walk(entry);
526
+ }
527
+ }
528
+ };
529
+ walk(output);
530
+ return output;
531
+ }
532
+ function buildAjvSchema(schema, strictMode) {
533
+ return strictMode ? applyStrictAdditionalProperties(schema) : schema;
534
+ }
456
535
  function formatErrors(errors) {
457
536
  return (errors ?? []).map((error) => {
458
537
  const location = error.instancePath || "/";
459
538
  return `${location} ${error.message ?? "failed validation"}`.trim();
460
539
  });
461
540
  }
541
+ function analyzeDrift(ajv, schema, body, options) {
542
+ const normalizedSchema = buildAjvSchema(schema, options.strictMode);
543
+ const validate = ajv.compile(normalizedSchema);
544
+ const valid = validate(body);
545
+ if (valid) {
546
+ return [];
547
+ }
548
+ const filteredErrors = (validate.errors ?? []).filter(
549
+ (entry) => !options.ignorePaths.some((rule) => pathMatchesIgnoreRule(entry.instancePath || "/", rule))
550
+ );
551
+ return formatErrors(filteredErrors);
552
+ }
462
553
  var DriftDetector = class {
463
- constructor(logger) {
554
+ constructor(logger, options) {
464
555
  this.logger = logger;
465
556
  addFormats(this.ajv);
557
+ this.strictMode = options?.strictMode ?? false;
558
+ this.ignorePaths = options?.ignorePaths ?? [];
466
559
  }
467
560
  ajv = new Ajv({ allErrors: true, strict: false });
468
- validate(method, path6, statusCode, schema, body) {
561
+ strictMode;
562
+ ignorePaths;
563
+ validate(method, path7, statusCode, schema, body) {
469
564
  if (!schema || body === void 0 || body === null) {
470
565
  return null;
471
566
  }
472
- const validate = this.ajv.compile(schema);
473
- const valid = validate(body);
474
- if (valid) {
567
+ const errors = analyzeDrift(this.ajv, schema, body, {
568
+ strictMode: this.strictMode,
569
+ ignorePaths: this.ignorePaths
570
+ });
571
+ if (!errors.length) {
475
572
  return null;
476
573
  }
477
574
  const issue = {
478
575
  method: method.toUpperCase(),
479
- path: path6,
576
+ path: path7,
480
577
  statusCode,
481
- message: `Drift detected for ${method.toUpperCase()} ${path6} (${statusCode})`,
482
- errors: formatErrors(validate.errors)
578
+ message: `Drift detected for ${method.toUpperCase()} ${path7} (${statusCode})`,
579
+ errors
483
580
  };
484
581
  const errorLines = issue.errors.map((entry) => ` \u2022 ${entry}`).join("\n");
485
- this.logger.error([
582
+ const prettyBlock = [
486
583
  `${pc.bgRed(pc.black(" \u{1F6A8} DRIFT DETECTED "))} ${pc.bold(issue.method)} ${issue.path} (${statusCode})`,
487
584
  errorLines
488
- ].join("\n"));
585
+ ].join("\n");
586
+ process.stderr.write(`${prettyBlock}
587
+ `);
588
+ this.logger.debug({ drift: issue }, issue.message);
489
589
  return issue;
490
590
  }
491
591
  };
@@ -635,7 +735,7 @@ async function loadOpenApiDocument(specPath) {
635
735
  }
636
736
 
637
737
  // src/state-store.ts
638
- import { mkdir as mkdir3, readFile, writeFile as writeFile3 } from "fs/promises";
738
+ import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
639
739
  import path4 from "path";
640
740
  function normalizeDatabase(input) {
641
741
  return {
@@ -676,7 +776,7 @@ var JsonStateStore = class {
676
776
  }
677
777
  }
678
778
  async read() {
679
- const raw = await readFile(this.filePath, "utf8");
779
+ const raw = await readFile2(this.filePath, "utf8");
680
780
  return normalizeDatabase(JSON.parse(raw));
681
781
  }
682
782
  async write(database) {
@@ -692,8 +792,8 @@ var JsonStateStore = class {
692
792
  };
693
793
 
694
794
  // src/proxy.ts
695
- async function proxyRequest(targetBaseUrl, path6, init) {
696
- const response = await fetch(new URL(path6, targetBaseUrl), init);
795
+ async function proxyRequest(targetBaseUrl, path7, init) {
796
+ const response = await fetch(new URL(path7, targetBaseUrl), init);
697
797
  const contentType = readContentType(response.headers);
698
798
  let body;
699
799
  let rawBody;
@@ -714,6 +814,56 @@ async function proxyRequest(targetBaseUrl, path6, init) {
714
814
  };
715
815
  }
716
816
 
817
+ // src/drift-reporter.ts
818
+ import { mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
819
+ import path5 from "path";
820
+ function escapeXml(value) {
821
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
822
+ }
823
+ function toJunit(issues) {
824
+ const testCases = issues.map((issue, index) => {
825
+ const name = `${issue.method} ${issue.path} (${issue.statusCode}) #${index + 1}`;
826
+ const failure = escapeXml(issue.errors.join("; "));
827
+ return ` <testcase classname="contract-drift-detection" name="${escapeXml(name)}">
828
+ <failure message="drift detected">${failure}</failure>
829
+ </testcase>`;
830
+ }).join("\n");
831
+ return [
832
+ '<?xml version="1.0" encoding="UTF-8"?>',
833
+ `<testsuite name="contract-drift-detection" tests="${issues.length}" failures="${issues.length}">`,
834
+ testCases,
835
+ "</testsuite>",
836
+ ""
837
+ ].join("\n");
838
+ }
839
+ function toJson(issues) {
840
+ const report = {
841
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
842
+ summary: {
843
+ total: issues.length
844
+ },
845
+ issues
846
+ };
847
+ return `${JSON.stringify(report, null, 2)}
848
+ `;
849
+ }
850
+ function defaultReportPath(config) {
851
+ if (config.reportFile) {
852
+ return config.reportFile;
853
+ }
854
+ return config.reporter === "junit" ? "cdd-drift-report.xml" : "cdd-drift-report.json";
855
+ }
856
+ async function writeDriftReport(config, issues) {
857
+ if (config.reporter === "pretty") {
858
+ return null;
859
+ }
860
+ const filePath = defaultReportPath(config);
861
+ const content = config.reporter === "junit" ? toJunit(issues) : toJson(issues);
862
+ await mkdir4(path5.dirname(filePath), { recursive: true });
863
+ await writeFile4(filePath, content, "utf8");
864
+ return filePath;
865
+ }
866
+
717
867
  // src/server.ts
718
868
  function toProxyHeaders(inputHeaders) {
719
869
  const passthrough = ["host", "content-length", "connection"];
@@ -886,7 +1036,7 @@ async function handleMockRoute(store, route, request) {
886
1036
  }
887
1037
  });
888
1038
  }
889
- async function handleProxyRoute(config, route, detector, request) {
1039
+ async function handleProxyRoute(config, request) {
890
1040
  const targetBaseUrl = config.driftCheckTarget;
891
1041
  if (!targetBaseUrl) {
892
1042
  throw new Error("Proxy target is not configured");
@@ -900,15 +1050,6 @@ async function handleProxyRoute(config, route, detector, request) {
900
1050
  headers,
901
1051
  body: toProxyBody(request)
902
1052
  });
903
- if (result.statusCode >= 200 && result.statusCode < 300) {
904
- detector.validate(
905
- route.method,
906
- route.path,
907
- result.statusCode,
908
- route.successResponse?.schema,
909
- result.body
910
- );
911
- }
912
1053
  return {
913
1054
  statusCode: result.statusCode,
914
1055
  headers: Object.fromEntries(result.headers.entries()),
@@ -917,7 +1058,11 @@ async function handleProxyRoute(config, route, detector, request) {
917
1058
  }
918
1059
  async function registerRoutes(app, document, config, store) {
919
1060
  const routes = buildRouteContexts(document);
920
- const detector = new DriftDetector(app.log);
1061
+ const detector = new DriftDetector(app.log, {
1062
+ strictMode: config.strictDrift,
1063
+ ignorePaths: config.driftIgnorePaths
1064
+ });
1065
+ const driftIssues = [];
921
1066
  for (const route of routes) {
922
1067
  app.route({
923
1068
  method: route.method.toUpperCase(),
@@ -925,7 +1070,21 @@ async function registerRoutes(app, document, config, store) {
925
1070
  handler: async (request, reply) => {
926
1071
  if (config.driftCheckTarget) {
927
1072
  try {
928
- const proxied = await handleProxyRoute(config, route, detector, request);
1073
+ const proxied = await handleProxyRoute(config, request);
1074
+ if (proxied.statusCode >= 200 && proxied.statusCode < 300 && proxied.body !== void 0) {
1075
+ const issue = detector.validate(
1076
+ route.method,
1077
+ route.path,
1078
+ proxied.statusCode,
1079
+ route.successResponse?.schema,
1080
+ proxied.body
1081
+ );
1082
+ if (issue) {
1083
+ driftIssues.push(issue);
1084
+ await writeDriftReport(config, driftIssues);
1085
+ await config.onDriftDetected?.(issue);
1086
+ }
1087
+ }
929
1088
  for (const [headerName, headerValue] of Object.entries(proxied.headers)) {
930
1089
  if (headerName.toLowerCase() === "content-length") {
931
1090
  continue;
@@ -955,6 +1114,10 @@ async function registerRoutes(app, document, config, store) {
955
1114
  hasDsl: Boolean(route.operation["x-mock-state"])
956
1115
  }))
957
1116
  );
1117
+ app.get("/__drift", async () => ({
1118
+ total: driftIssues.length,
1119
+ issues: driftIssues
1120
+ }));
958
1121
  }
959
1122
  async function createServer(config) {
960
1123
  const app = Fastify({
@@ -970,7 +1133,7 @@ async function createServer(config) {
970
1133
  });
971
1134
  const document = await loadOpenApiDocument(config.specPath);
972
1135
  const seedCollections = inferSeedCollections(document);
973
- const dbPath = resolveFile(path5.dirname(config.specPath), config.dbPath);
1136
+ const dbPath = resolveFile(path6.dirname(config.specPath), config.dbPath);
974
1137
  const store = new JsonStateStore(dbPath);
975
1138
  await store.initialize(seedCollections);
976
1139
  app.get("/__health", async () => ({ status: "ok" }));
@@ -986,8 +1149,12 @@ function renderStartupBanner(config) {
986
1149
  `- Spec: ${config.specPath}`,
987
1150
  `- DB: ${config.dbPath}`,
988
1151
  `- Mode: ${config.driftCheckTarget ? `proxy + drift-check (${config.driftCheckTarget})` : "stateful mock"}`,
1152
+ `- Drift policy: strict=${config.strictDrift ? "on" : "off"}, fail-on-drift=${config.failOnDrift ? "on" : "off"}`,
1153
+ `- Reporter: ${config.reporter}${config.reportFile ? ` (${config.reportFile})` : ""}`,
1154
+ `- Ignore rules: ${config.driftIgnorePaths.length ? config.driftIgnorePaths.join(", ") : "none"}`,
989
1155
  "- Health: GET /__health",
990
- "- Routes: GET /__routes"
1156
+ "- Routes: GET /__routes",
1157
+ "- Drift: GET /__drift"
991
1158
  ];
992
1159
  return `${lines.join("\n")}
993
1160
  `;
@@ -998,10 +1165,21 @@ async function main() {
998
1165
  const initCommand = cli.commands.find((command) => command.name() === "init");
999
1166
  const quickstartCommand = cli.commands.find((command) => command.name() === "quickstart");
1000
1167
  const startServer = async (rawOptions) => {
1001
- const config = await resolveServeConfig(process.cwd(), rawOptions);
1168
+ let hasFailedOnDrift = false;
1169
+ const config = await resolveServeConfig(process2.cwd(), rawOptions);
1170
+ config.onDriftDetected = async () => {
1171
+ if (!config.failOnDrift || hasFailedOnDrift) {
1172
+ return;
1173
+ }
1174
+ hasFailedOnDrift = true;
1175
+ process2.stderr.write("Failing process because drift was detected (--fail-on-drift enabled).\n");
1176
+ setTimeout(() => {
1177
+ process2.exit(1);
1178
+ }, 25);
1179
+ };
1002
1180
  const server = await createServer(config);
1003
1181
  await server.listen({ port: config.port, host: config.host });
1004
- process.stdout.write(renderStartupBanner(config));
1182
+ process2.stdout.write(renderStartupBanner(config));
1005
1183
  };
1006
1184
  serveCommand?.action(async function() {
1007
1185
  await startServer(this.opts());
@@ -1016,19 +1194,19 @@ async function main() {
1016
1194
  });
1017
1195
  initCommand?.action(async function() {
1018
1196
  const options = this.opts();
1019
- const targetPath = await writeStarterConfig(process.cwd(), {
1197
+ const targetPath = await writeStarterConfig(process2.cwd(), {
1020
1198
  spec: String(options.spec),
1021
1199
  db: String(options.db),
1022
1200
  host: String(options.host),
1023
1201
  port: Number(options.port),
1024
1202
  template: options.template === "none" ? "none" : "rest-crud"
1025
1203
  });
1026
- process.stdout.write(`Created ${targetPath}
1204
+ process2.stdout.write(`Created ${targetPath}
1027
1205
  `);
1028
1206
  });
1029
1207
  quickstartCommand?.action(async function() {
1030
1208
  const options = this.opts();
1031
- const cwd = process.cwd();
1209
+ const cwd = process2.cwd();
1032
1210
  const specPath = String(options.spec ?? "openapi.yaml");
1033
1211
  await writeStarterConfig(cwd, {
1034
1212
  spec: specPath,
@@ -1047,22 +1225,22 @@ async function main() {
1047
1225
  });
1048
1226
  const server = await createServer(config);
1049
1227
  await server.listen({ port: config.port, host: config.host });
1050
- process.stdout.write(renderStartupBanner(config));
1228
+ process2.stdout.write(renderStartupBanner(config));
1051
1229
  });
1052
- await cli.parseAsync(process.argv);
1230
+ await cli.parseAsync(process2.argv);
1053
1231
  }
1054
1232
  await main().catch((error) => {
1055
1233
  if (error instanceof Error) {
1056
- process.stderr.write(`Error: ${error.message}
1234
+ process2.stderr.write(`Error: ${error.message}
1057
1235
  `);
1058
- if (process.env.CDD_SHOW_STACK === "1") {
1059
- process.stderr.write(`${error.stack}
1236
+ if (process2.env.CDD_SHOW_STACK === "1") {
1237
+ process2.stderr.write(`${error.stack}
1060
1238
  `);
1061
1239
  }
1062
1240
  } else {
1063
- process.stderr.write(`${String(error)}
1241
+ process2.stderr.write(`${String(error)}
1064
1242
  `);
1065
1243
  }
1066
- process.exitCode = 1;
1244
+ process2.exitCode = 1;
1067
1245
  });
1068
1246
  //# sourceMappingURL=cli.js.map