UNPKG

@informalsystems/quint

Version:

Core tool for the Quint specification language

551 lines 27.3 kB
"use strict"; /** * The commands for the quint CLI * * See the description at: * https://github.com/informalsystems/quint/blob/main/doc/quint.md * * @author Igor Konnov, Gabriela Moreira, Shon Feder, Informal Systems, 2021-2025 */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.docs = exports.outputResult = exports.outputCompilationTarget = exports.verifySpec = exports.compile = exports.runSimulator = exports.runTests = exports.runRepl = exports.typecheck = exports.parse = exports.load = void 0; const fs_1 = require("fs"); const path_1 = require("path"); const process_1 = require("process"); const chalk_1 = __importDefault(require("chalk")); const quintParserFrontend_1 = require("./parsing/quintParserFrontend"); const either_1 = require("@sweet-monads/either"); const repl_1 = require("./repl"); const errorReporter_1 = require("./errorReporter"); const docs_1 = require("./docs"); const idGenerator_1 = require("./idGenerator"); const simulation_1 = require("./simulation"); const verbosity_1 = require("./verbosity"); const sourceResolver_1 = require("./parsing/sourceResolver"); const quintVerifier_1 = require("./quintVerifier"); const fullFlattener_1 = require("./flattening/fullFlattener"); const quintAnalyzer_1 = require("./quintAnalyzer"); const trace_1 = require("./runtime/trace"); const lodash_1 = require("lodash"); const maybe_1 = require("@sweet-monads/maybe"); const compileToTlaplus_1 = require("./compileToTlaplus"); const evaluator_1 = require("./runtime/impl/evaluator"); const initToPredicate_1 = require("./ir/initToPredicate"); const quintRustWrapper_1 = require("./quintRustWrapper"); const cliReporting_1 = require("./cliReporting"); const cliHelpers_1 = require("./cliHelpers"); const assert_1 = require("assert"); const rng_1 = require("./rng"); const apalache_1 = require("./apalache"); /** Load a file into a string * * @param args the CLI arguments parsed by yargs */ async function load(args) { const stage = { stage: 'loading', args }; if ((0, fs_1.existsSync)(args.input)) { try { const path = (0, path_1.resolve)((0, process_1.cwd)(), args.input); const sourceCode = (0, fs_1.readFileSync)(path, 'utf8'); return (0, either_1.right)({ ...stage, args, path, sourceCode: new Map([[path, sourceCode]]), warnings: [], }); } catch (err) { return (0, cliReporting_1.cliErr)(`file ${args.input} could not be opened due to ${err}`, { ...stage, errors: [], sourceCode: new Map(), }); } } else { return (0, cliReporting_1.cliErr)(`file ${args.input} does not exist`, { ...stage, errors: [], sourceCode: new Map() }); } } exports.load = load; /** * Parse a Quint specification. * * @param loaded the procedure stage produced by `load` */ async function parse(loaded) { const { args, sourceCode, path } = loaded; const text = sourceCode.get(path); const parsing = { ...loaded, stage: 'parsing' }; const idGen = (0, idGenerator_1.newIdGenerator)(); return (0, lodash_1.flow)([ () => { const phase1Data = (0, quintParserFrontend_1.parsePhase1fromText)(idGen, text, path); // if there is exactly one module in the original text, make it the main one const defaultModuleName = phase1Data.modules.length === 1 ? (0, maybe_1.just)(phase1Data.modules[0].name) : (0, maybe_1.none)(); return { ...phase1Data, defaultModuleName }; }, phase1Data => { const resolver = (0, sourceResolver_1.fileSourceResolver)(sourceCode); const mainPath = resolver.lookupPath((0, path_1.dirname)(path), (0, path_1.basename)(path)); return (0, quintParserFrontend_1.parsePhase2sourceResolution)(idGen, resolver, mainPath, phase1Data); }, phase2Data => { if (args.sourceMap) { // Write source map to the specified file (0, cliReporting_1.writeToJson)(args.sourceMap, (0, quintParserFrontend_1.compactSourceMap)(phase2Data.sourceMap)); } return (0, quintParserFrontend_1.parsePhase3importAndNameResolution)(phase2Data); }, phase3Data => (0, quintParserFrontend_1.parsePhase4toposort)(phase3Data), phase4Data => ({ ...parsing, ...phase4Data, idGen }), result => { if (result.errors.length > 0) { const newErrorMessages = result.errors.map((0, cliHelpers_1.mkErrorMessage)(result.sourceMap)); const errorMessages = parsing.errors ? parsing.errors.concat(newErrorMessages) : newErrorMessages; return (0, either_1.left)({ msg: 'parsing failed', stage: { ...result, errors: errorMessages } }); } return (0, either_1.right)(result); }, ])(); } exports.parse = parse; /** * Check types and effects of a Quint specification. * * @param parsed the procedure stage produced by `parse` */ async function typecheck(parsed) { const { table, modules, sourceMap } = parsed; const [errorMap, result] = (0, quintAnalyzer_1.analyzeModules)(table, modules); const typechecking = { ...parsed, ...result, stage: 'typechecking' }; if (errorMap.length === 0) { return (0, either_1.right)(typechecking); } else { const errors = errorMap.map((0, cliHelpers_1.mkErrorMessage)(sourceMap)); return (0, cliReporting_1.cliErr)('typechecking failed', { ...typechecking, errors }); } } exports.typecheck = typecheck; /** * Run REPL. * * @param argv parameters as provided by yargs */ async function runRepl(argv) { let filename = undefined; let moduleName = undefined; if (argv.require) { // quint -r FILE.qnt or quint -r FILE.qnt::MODULE const m = /^(.*?)(?:|::([a-zA-Z_]\w*))$/.exec(argv.require); if (m) { ; [filename, moduleName] = m.slice(1, 3); } } const options = { preloadFilename: filename, importModule: moduleName, replInput: argv.commands, verbosity: argv.quiet ? 0 : argv.verbosity, }; (0, repl_1.quintRepl)(process.stdin, process.stdout, options); } exports.runRepl = runRepl; /** * Run the tests. We imitate the output of mocha. * * @param typedStage the procedure stage produced by `typecheck` */ /** * Main function to run tests. */ async function runTests(prev) { const testing = { ...prev, stage: 'testing' }; const verbosityLevel = (0, cliHelpers_1.deriveVerbosity)(prev.args); const mainName = (0, cliHelpers_1.guessMainModule)(prev); const main = (0, cliReporting_1.findMainModule)(prev, mainName); if (!main) { return (0, cliReporting_1.handleMainModuleError)(prev, mainName); } const options = { testMatch: (n) => (0, cliHelpers_1.isMatchingTest)(prev.args.match, n), maxSamples: prev.args.maxSamples, rng: (0, rng_1.newRng)(prev.args.seed), verbosity: verbosityLevel, onTrace: (0, cliReporting_1.prepareOnTrace)(prev.args.input, prev.args.outItf, prev.args.nTraces, false), }; const startMs = Date.now(); if (verbosity_1.verbosity.hasResults(verbosityLevel)) { console.log(`\n ${mainName}`); } const testDefs = Array.from(prev.resolver.collector.definitionsByModule.get(mainName).values()) .flat() .filter(d => d.kind === 'def' && options.testMatch(d.name)); const evaluator = new evaluator_1.Evaluator(prev.table, (0, trace_1.newTraceRecorder)(verbosityLevel, options.rng, 1), options.rng); const results = testDefs.map((def, index) => evaluator.test(def, options.maxSamples, index, options.onTrace)); const elapsedMs = Date.now() - startMs; (0, cliReporting_1.outputTestResults)(results, verbosityLevel, elapsedMs); const passed = results.filter(r => r.status === 'passed'); const failed = results.filter(r => r.status === 'failed'); const ignored = results.filter(r => r.status === 'ignored'); const stage = { ...testing, passed: passed.map(r => r.name), failed: failed.map(r => r.name), ignored: ignored.map(r => r.name), errors: [], }; if (failed.length === 0) { return (0, either_1.right)(stage); } (0, cliReporting_1.outputTestErrors)(stage, verbosityLevel, failed); return (0, cliReporting_1.cliErr)('Tests failed', stage); } exports.runTests = runTests; /** * Run the simulator. * * @param prev the procedure stage produced by `typecheck` */ async function runSimulator(prev) { const simulator = { ...prev, stage: 'running' }; const startMs = Date.now(); // Verboity level controls how much of the output is shown const verbosityLevel = (0, cliHelpers_1.deriveVerbosity)(prev.args); const mainName = (0, cliHelpers_1.guessMainModule)(prev); const main = prev.modules.find(m => m.name === mainName); if (!main) { return (0, cliReporting_1.handleMainModuleError)(prev, mainName); } const rng = (0, rng_1.newRng)(prev.args.seed); // We use: // - 'invariantString' as the combined invariant string for the simulator to check // - 'individualInvariants' for reporting which specific invariants were violated const [invariantString, invariantsList] = (0, cliHelpers_1.getInvariants)(prev.args); const individualInvariants = invariantsList.length > 0 ? invariantsList : ['true']; const options = { init: prev.args.init, step: prev.args.step, invariant: invariantString, individualInvariants: individualInvariants, maxSamples: prev.args.maxSamples, maxSteps: prev.args.maxSteps, rng, verbosity: verbosityLevel, storeMetadata: prev.args.mbt, hideVars: prev.args.hide || [], numberOfTraces: prev.args.nTraces, onTrace: (0, cliReporting_1.prepareOnTrace)(prev.args.input, prev.args.outItf, prev.args.nTraces, prev.args.mbt), }; const recorder = (0, trace_1.newTraceRecorder)(options.verbosity, options.rng, options.numberOfTraces); const argsParsingResult = (0, either_1.mergeInMany)([prev.args.init, prev.args.step, invariantString, ...prev.args.witnesses].map(e => (0, cliHelpers_1.toExpr)(prev, e))); if (argsParsingResult.isLeft()) { return (0, cliReporting_1.cliErr)('Argument error', { ...simulator, errors: argsParsingResult.value.map((0, cliHelpers_1.mkErrorMessage)(new Map())), }); } const [init, step, invariant, ...witnesses] = argsParsingResult.value; let outcome; if (prev.args.backend == 'rust') { if (prev.args.mbt || prev.args.witnesses.length > 0) { console.warn(chalk_1.default.yellow('Warning: --mbt and --witnesses are ignored when using the Rust backend (at this time).')); console.warn(chalk_1.default.yellow('Use the typescript backend if you need that functionality.')); } // Parse the combined invariant for the Rust backend const invariantExpr = (0, cliHelpers_1.toExpr)(prev, invariantString); if (invariantExpr.isLeft()) { return (0, cliReporting_1.cliErr)('Argument error', { ...simulator, errors: [(0, cliHelpers_1.mkErrorMessage)(prev.sourceMap)(invariantExpr.value)], }); } const quintRustWrapper = new quintRustWrapper_1.QuintRustWrapper(verbosityLevel); const nThreads = Math.min(prev.args.maxSamples, prev.args.nThreads); outcome = await quintRustWrapper.simulate({ modules: [], table: prev.resolver.table, main: mainName, init, step, invariant: invariantExpr.value }, prev.path, witnesses, prev.args.maxSamples, prev.args.maxSteps, prev.args.nTraces ?? 1, nThreads, prev.args.seed, options.onTrace); } else { // Use the typescript simulator const evaluator = new evaluator_1.Evaluator(prev.resolver.table, recorder, options.rng, options.storeMetadata); outcome = evaluator.simulate(init, step, invariant, witnesses, prev.args.maxSamples, prev.args.maxSteps, prev.args.nTraces ?? 1, options.onTrace); } const elapsedMs = Date.now() - startMs; simulator.seed = outcome.bestTraces[0]?.seed; const states = outcome.bestTraces[0]?.states; const frames = recorder.bestTraces[0]?.frame?.subframes; switch (outcome.status) { case 'error': return (0, cliReporting_1.cliErr)('Runtime error', { ...simulator, status: outcome.status, seed: prev.args.seed, errors: outcome.errors.map((0, cliHelpers_1.mkErrorMessage)(prev.sourceMap)), }); case 'ok': (0, cliReporting_1.maybePrintCounterExample)(verbosityLevel, states, frames, prev.args.hide || []); if (verbosity_1.verbosity.hasResults(verbosityLevel)) { console.log(chalk_1.default.green('[ok]') + ' No violation found ' + chalk_1.default.gray(`(${elapsedMs}ms at ${Math.round((1000 * outcome.samples) / elapsedMs)} traces/second).`)); if (verbosity_1.verbosity.hasHints(verbosityLevel)) { console.log(chalk_1.default.gray((0, simulation_1.showTraceStatistics)(outcome.traceStatistics))); console.log(chalk_1.default.gray('You may increase --max-samples and --max-steps.')); console.log(chalk_1.default.gray('Use --verbosity to produce more (or less) output.')); } } (0, cliReporting_1.maybePrintWitnesses)(verbosityLevel, outcome, prev.args.witnesses); return (0, either_1.right)({ ...simulator, status: outcome.status, trace: states, }); case 'violation': (0, cliReporting_1.maybePrintCounterExample)(verbosityLevel, states, frames, prev.args.hide || []); if (verbosity_1.verbosity.hasResults(verbosityLevel)) { console.log(chalk_1.default.red(`[violation]`) + ' Found an issue ' + chalk_1.default.gray(`(${elapsedMs}ms at ${Math.round((1000 * outcome.samples) / elapsedMs)} traces/second).`)); (0, cliReporting_1.printViolatedInvariants)(states[states.length - 1], individualInvariants, prev); } if (verbosity_1.verbosity.hasHints(verbosityLevel)) { console.log(chalk_1.default.gray('Use --verbosity=3 to show executions.')); } (0, cliReporting_1.maybePrintWitnesses)(verbosityLevel, outcome, prev.args.witnesses); return (0, cliReporting_1.cliErr)('Invariant violated', { ...simulator, status: outcome.status, trace: states, errors: [], }); } } exports.runSimulator = runSimulator; /** Compile to a flattened module, that includes the special q::* declarations * * @param typechecked the output of a preceding type checking stage */ async function compile(typechecked) { const args = typechecked.args; const mainName = (0, cliHelpers_1.guessMainModule)(typechecked); const main = typechecked.modules.find(m => m.name === mainName); if (!main) { return (0, cliReporting_1.cliErr)(`module ${mainName} does not exist`, { ...typechecked, errors: [], sourceCode: new Map() }); } const extraDefsAsText = [`action q::init = ${args.init}`, `action q::step = ${args.step}`]; const [invariantString, invariantsList] = (0, cliHelpers_1.getInvariants)(typechecked.args); if (invariantsList.length > 0) { extraDefsAsText.push(`val q::inv = and(${invariantString})`); } if (args.inductiveInvariant) { extraDefsAsText.push(`val q::inductiveInv = ${args.inductiveInvariant}`); } if (args.temporal) { extraDefsAsText.push(`temporal q::temporalProps = and(${args.temporal})`); } const extraDefs = extraDefsAsText.map(d => (0, quintParserFrontend_1.parseDefOrThrow)(d, typechecked.idGen, new Map())); main.declarations.push(...extraDefs); // We have to update the lookup table and analysis result with the new definitions. This is not ideal, and the problem // is that is hard to add this definitions in the proper stage, in our current setup. We should try to tackle this // while solving #1052. const resolutionResult = (0, quintParserFrontend_1.parsePhase3importAndNameResolution)({ ...typechecked, errors: [] }); if (resolutionResult.errors.length > 0) { const errors = resolutionResult.errors.map((0, cliHelpers_1.mkErrorMessage)(typechecked.sourceMap)); return (0, cliReporting_1.cliErr)('name resolution failed', { ...typechecked, errors }); } typechecked.table = resolutionResult.table; (0, quintAnalyzer_1.analyzeInc)(typechecked, typechecked.table, extraDefs); // CANNOT be `if (!args.flatten)`, we need to make sure it's a boolean value if (args.flatten === false) { if (args.target === 'tlaplus') { console.warn(chalk_1.default.yellow('Warning: flattening is required for TLA+ output, ignoring --flatten=false option.')); } else { // Early return with the original (unflattened) module and its fields return (0, either_1.right)({ ...typechecked, mainModule: main, main: mainName, stage: 'compiling', }); } } // Flatten modules, replacing instances, imports and exports with their definitions const { flattenedModules, flattenedTable, flattenedAnalysis } = (0, fullFlattener_1.flattenModules)(typechecked.modules, typechecked.table, typechecked.idGen, typechecked.sourceMap, typechecked); // Pick the main module const flatMain = flattenedModules.find(m => m.name === mainName); return (0, either_1.right)({ ...typechecked, ...flattenedAnalysis, mainModule: flatMain, table: flattenedTable, main: mainName, stage: 'compiling', }); } exports.compile = compile; /** * Verify a spec via Apalache. * * @param prev the procedure stage produced by `typecheck` */ async function verifySpec(prev) { const verifying = { ...prev, stage: 'verifying' }; const args = verifying.args; const verbosityLevel = (0, cliHelpers_1.deriveVerbosity)(prev.args); const itfFile = prev.args.outItf; if (itfFile) { if (itfFile.includes(cliHelpers_1.PLACEHOLDERS.test) || itfFile.includes(cliHelpers_1.PLACEHOLDERS.seq)) { console.log(`${chalk_1.default.yellow('[warning]')} the output file contains ${chalk_1.default.grey(cliHelpers_1.PLACEHOLDERS.test)} or ${chalk_1.default.grey(cliHelpers_1.PLACEHOLDERS.seq)}, but this has no effect since at most a single trace will be produced.`); } } const loadedConfig = (0, cliHelpers_1.loadApalacheConfig)(verifying, args.apalacheConfig); const veryfiyingFlat = { ...prev, modules: [prev.mainModule] }; const parsedSpec = (0, cliReporting_1.outputJson)(veryfiyingFlat); const [invariantsString, invariantsList] = (0, cliHelpers_1.getInvariants)(prev.args); if (args.inductiveInvariant) { const hasOrdinaryInvariant = invariantsList.length > 0; const nPhases = hasOrdinaryInvariant ? 3 : 2; const initConfig = (0, apalache_1.createConfig)(loadedConfig, parsedSpec, { ...args, maxSteps: 0 }, ['q::inductiveInv']); // Checking whether the inductive invariant holds in the initial state(s) (0, cliReporting_1.printInductiveInvariantProgress)(verbosityLevel, args, 1, nPhases); const startMs = Date.now(); return (0, quintVerifier_1.verify)(args.serverEndpoint, args.apalacheVersion, initConfig, verbosityLevel).then(res => { if (res.isLeft()) { return (0, cliReporting_1.processVerifyResult)(res, startMs, verbosityLevel, verifying, [args.inductiveInvariant]); } // Checking whether the inductive invariant is preserved by the step (0, cliReporting_1.printInductiveInvariantProgress)(verbosityLevel, args, 2, nPhases); const stepConfig = (0, apalache_1.createConfig)(loadedConfig, parsedSpec, { ...args, maxSteps: 1 }, ['q::inductiveInv'], 'q::inductiveInv'); return (0, quintVerifier_1.verify)(args.serverEndpoint, args.apalacheVersion, stepConfig, verbosityLevel).then(res => { if (res.isLeft() || !hasOrdinaryInvariant) { return (0, cliReporting_1.processVerifyResult)(res, startMs, verbosityLevel, verifying, [args.inductiveInvariant]); } // Checking whether the inductive invariant implies the ordinary invariant (0, cliReporting_1.printInductiveInvariantProgress)(verbosityLevel, args, 3, nPhases, invariantsString); const propConfig = (0, apalache_1.createConfig)(loadedConfig, parsedSpec, { ...args, maxSteps: 0 }, ['q::inv'], 'q::inductiveInv'); return (0, quintVerifier_1.verify)(args.serverEndpoint, args.apalacheVersion, propConfig, verbosityLevel).then(res => { return (0, cliReporting_1.processVerifyResult)(res, startMs, verbosityLevel, verifying, invariantsList); }); }); }); } // We need to insert the data form CLI args into their appropriate locations // in the Apalache config const config = (0, apalache_1.createConfig)(loadedConfig, parsedSpec, args, invariantsList.length > 0 ? ['q::inv'] : []); const startMs = Date.now(); return (0, quintVerifier_1.verify)(args.serverEndpoint, args.apalacheVersion, config, verbosityLevel).then(res => { return (0, cliReporting_1.processVerifyResult)(res, startMs, verbosityLevel, verifying, invariantsList); }); } exports.verifySpec = verifySpec; /** output a compiled spec in the format specified in the `compiled.args.target` to stdout * * @param compiled The result of a preceding compile stage */ async function outputCompilationTarget(compiled) { const stage = 'outputting target'; const args = compiled.args; const verbosityLevel = (0, cliHelpers_1.deriveVerbosity)(args); const target = compiled.args.target.toLowerCase(); const removeRuns = (module) => { return { ...module, declarations: module.declarations.filter(d => d.kind !== 'def' || d.qualifier !== 'run') }; }; const main = target == 'tlaplus' ? (0, initToPredicate_1.convertInit)(removeRuns(compiled.mainModule), compiled.table, compiled.modes) : (0, either_1.right)(compiled.mainModule); if (main.isLeft()) { return (0, cliReporting_1.cliErr)('Failed to convert init to predicate', { ...compiled, errors: main.value.map((0, cliHelpers_1.mkErrorMessage)(compiled.sourceMap)), }); } const parsedSpecJson = (0, cliReporting_1.outputJson)({ ...compiled, modules: [main.value], table: compiled.table }); switch (target) { case 'json': process.stdout.write(parsedSpecJson); return (0, either_1.right)(compiled); case 'tlaplus': { const toTlaResult = await (0, compileToTlaplus_1.compileToTlaplus)(args.serverEndpoint, args.apalacheVersion, parsedSpecJson, verbosityLevel); return toTlaResult .mapRight(tla => { process.stdout.write(tla); // Write out, since all went right return compiled; }) .mapLeft(err => { return { msg: err.explanation, stage: { ...compiled, stage, status: 'error', errors: err.errors }, }; }); } default: // This is validated in the arg parsing (0, assert_1.fail)(`Invalid option for --target`); } } exports.outputCompilationTarget = outputCompilationTarget; /** Write the OutputStage of the procedureStage as JSON, if --out is set * Otherwise, report any stage errors to STDOUT */ function outputResult(result) { result .map(stage => { const verbosityLevel = (0, cliHelpers_1.deriveVerbosity)(stage.args); if (stage.args.out) { (0, cliReporting_1.writeOutputToJson)(stage.args.out, stage); } else if (!stage.args.outItf && stage.seed && verbosity_1.verbosity.hasResults(verbosityLevel)) { const backend = stage.args.backend ?? 'typescript'; console.log(chalk_1.default.gray(`Use --seed=0x${stage.seed.toString(16)} --backend=${backend} to reproduce.`)); } process.exit(0); }) .mapLeft(({ msg, stage }) => { const { args, errors, sourceCode } = stage; const verbosityLevel = (0, cliHelpers_1.deriveVerbosity)(args); if (args.out) { (0, cliReporting_1.writeOutputToJson)(args.out, stage); } else { const finders = (0, errorReporter_1.createFinders)(sourceCode); (0, lodash_1.uniqWith)(errors, lodash_1.isEqual).forEach(err => console.error((0, errorReporter_1.formatError)(sourceCode, finders, err))); if (!stage.args.outItf && stage.seed && verbosity_1.verbosity.hasResults(verbosityLevel)) { const backend = stage.args.backend ?? 'typescript'; console.log(chalk_1.default.gray(`Use --seed=0x${stage.seed.toString(16)} --backend=${backend} to reproduce.`)); } console.error(`error: ${msg}`); } process.exit(1); }); } exports.outputResult = outputResult; /** * Produces documentation from docstrings in a Quint specification. * * @param loaded the procedure stage produced by `load` */ async function docs(loaded) { const { sourceCode, path } = loaded; const text = sourceCode.get(path); const parsing = { ...loaded, stage: 'documentation' }; const phase1Data = (0, quintParserFrontend_1.parsePhase1fromText)((0, idGenerator_1.newIdGenerator)(), text, path); const allEntries = phase1Data.modules.map(module => { const documentationEntries = (0, docs_1.produceDocs)(module); const title = `# Documentation for ${module.name}\n\n`; const markdown = title + [...documentationEntries.values()].map(docs_1.toMarkdown).join('\n\n'); console.log(markdown); return [module.name, documentationEntries]; }); if (phase1Data.errors.length > 0) { const newErrorMessages = phase1Data.errors.map((0, cliHelpers_1.mkErrorMessage)(phase1Data.sourceMap)); const errorMessages = parsing.errors ? parsing.errors.concat(newErrorMessages) : newErrorMessages; return (0, either_1.left)({ msg: 'parsing failed', stage: { ...parsing, errors: errorMessages } }); } return (0, either_1.right)({ ...parsing, documentation: new Map(allEntries) }); } exports.docs = docs; //# sourceMappingURL=cliCommands.js.map