From b7d029ab695ca4fdffd41cd312c27660e7a89b4e Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Tue, 16 Apr 2024 23:39:04 +0800 Subject: [PATCH] MDL-81125 core: Add tooling to generate developer upgrade notes Usage: node .grunt/upgradenotes.mjs OR npm run upgradenote Arguments can be provided to specify the issue number, component, message, and type of change. node .grunt/upgradenotes.mjs -- \ -c [component] \ -i [issue number] \ -t [type] \ -m [message] OR npm run upgradenote -- \ -c [component] \ -i [issue number] \ -t [type] \ -m [message] Summary Markdown files can be created using: node .grunt/upgradenotes.mjs summary Weekly release files can be created using: node .grunt/upgradenotes.mjs release Final release files can be created using: using: node .grunt/upgradenotes.mjs release [version] Generally speaking the integration team will be responsible for updating the UPGRADING.md notes as part of the weekly integration processes. --- .gitignore | 1 + .grunt/notes/src/components.mjs | 110 +++ .grunt/notes/src/create.mjs | 85 ++ .grunt/notes/src/generate.mjs | 318 ++++++++ .grunt/notes/src/helpers.mjs | 230 ++++++ .grunt/notes/src/logger.mjs | 30 + .grunt/notes/src/note.mjs | 183 +++++ .grunt/notes/src/noteTypes.mjs | 45 ++ .grunt/notes/src/prompts.mjs | 153 ++++ .grunt/upgradenotes.mjs | 103 +++ .upgradenotes/.gitkeep | 0 UPGRADING.md | 7 + npm-shrinkwrap.json | 1313 ++++++++++++++++++++++++++++--- package.json | 8 + 14 files changed, 2485 insertions(+), 101 deletions(-) create mode 100644 .grunt/notes/src/components.mjs create mode 100644 .grunt/notes/src/create.mjs create mode 100644 .grunt/notes/src/generate.mjs create mode 100644 .grunt/notes/src/helpers.mjs create mode 100644 .grunt/notes/src/logger.mjs create mode 100644 .grunt/notes/src/note.mjs create mode 100644 .grunt/notes/src/noteTypes.mjs create mode 100644 .grunt/notes/src/prompts.mjs create mode 100755 .grunt/upgradenotes.mjs create mode 100644 .upgradenotes/.gitkeep create mode 100644 UPGRADING.md diff --git a/.gitignore b/.gitignore index 2ab8b0a6666..1f1ea578ac1 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,4 @@ moodle-plugin-ci.phar .hugo_build.lock phpcs.xml jsconfig.json +UPGRADING-CURRENT.md diff --git a/.grunt/notes/src/components.mjs b/.grunt/notes/src/components.mjs new file mode 100644 index 00000000000..f328766dfed --- /dev/null +++ b/.grunt/notes/src/components.mjs @@ -0,0 +1,110 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +import * as Components from '../../components.js'; + +const componentData = Components.fetchComponentData(); + +/** + * The standard components shipped with core Moodle. + * + * @type {Object} + */ +export const standardComponents = componentData.standardComponents; + +/** + * All components of the current Moodle instance. + * + * @type {Object} + */ +export const allComponents = componentData.components; + +/** + * Get all components of the current Moodle instance. + * + * @returns {Array} + */ +export const getAllComponents = () => { + let components = new Map(Object.entries(componentData.pluginTypes).map(([value, path]) => ([path,{ + path, + value, + name: `${value} (plugin type)`, + }]))); + + Object + .entries(componentData.components) + .filter(([path, value]) => Object.values(componentData.standardComponents).includes(value)) + .forEach(([path, value]) => { + const entry = { + path, + value, + name: value, + }; + if (Object.values(componentData.subsystems).includes(value)) { + if (components.has(path)) { + entry.name = `${value} (subsystem / plugintype)`; + } else { + entry.name = `${value} (subsystem)`; + } + } + + components.set(path, entry); + }); + + return Array.from(components.values()); +}; + +/** + * Whether the specified component is a standard component shipped with core Moodle. + * + * @param {string} componentName + * @returns {boolean} + */ +export const isStandardComponent = (componentName) => { + if (Object.values(componentData.standardComponents).includes(componentName)) { + return true; + } + + if (Object.keys(componentData.pluginTypes).includes(componentName)) { + return true; + } + + return false; +}; + +export const rewritePlugintypeAsSubsystem = (componentName) => { + if (Object.keys(componentData.pluginTypes).includes(componentName)) { + const pluginTypePath = componentData.pluginTypes[componentName]; + if (Object.keys(componentData.subsystems).includes(pluginTypePath)) { + return true; + } + } + + return false; +} + +/** + * Whether the specified component is a community component. + * + * @param {string} componentName + * @returns {boolean} + */ +export const isCommunityComponent = (componentName) => { + if (isStandardComponent(componentName)) { + return false; + } + + return Object.values(componentData.components).indexOf(componentName) !== -1; +} diff --git a/.grunt/notes/src/create.mjs b/.grunt/notes/src/create.mjs new file mode 100644 index 00000000000..70c41532db8 --- /dev/null +++ b/.grunt/notes/src/create.mjs @@ -0,0 +1,85 @@ +#!/usr/bin/env node +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +import inquirer from 'inquirer'; +import chalk from 'chalk'; + +import { createNote } from './note.mjs'; +import { getInitialValues } from './helpers.mjs'; +import * as Prompts from './prompts.mjs'; +import logger from './logger.mjs'; + +export default async (options) => { + // Processs the initial values. + const initialValues = getInitialValues(options); + + // Fetch information. + const messages = []; + const { issueNumber } = await inquirer.prompt([ + Prompts.getIssuePrompt(), + ], initialValues); + + let selection = {}; + let notePath; + do { + selection = {}; + selection = await inquirer.prompt([ + Prompts.getComponentsPrompt(), + Prompts.getTypePrompt(), + Prompts.getMessagePromptInput(), + ], initialValues); + if (selection.message === '') { + selection = Object.assign( + selection, + await inquirer.prompt([ + Prompts.getMessagePromptEditor(), + ]), + ); + } + + logger.info(` + Creating upgrade note with the following options: + + - Issue: ${chalk.bold(issueNumber)} + - Component: ${chalk.bold(selection.components)} + - Type: ${chalk.bold(selection.type)} + - Message: + ${chalk.bold(selection.message)} +`); + + messages.push({ + components: [selection.components], + type: selection.type, + message: selection.message, + }); + + // Save the note so far. + if (notePath) { + await createNote(issueNumber, messages, notePath); + logger.info(`Updated note at: ${chalk.underline(chalk.bold(notePath))}`); + } else { + notePath = await createNote(issueNumber, messages); + logger.info(`Note created at: ${chalk.underline(chalk.bold(notePath))}`); + } + + selection = Object.assign( + selection, + await inquirer.prompt([ + Prompts.getAddAnotherPrompt(), + ], initialValues), + ); + } while (selection.addAnother); +}; diff --git a/.grunt/notes/src/generate.mjs b/.grunt/notes/src/generate.mjs new file mode 100644 index 00000000000..8a09419e121 --- /dev/null +++ b/.grunt/notes/src/generate.mjs @@ -0,0 +1,318 @@ +#!/usr/bin/env node +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +import chalk from 'chalk'; +import { getAllComponents } from './components.mjs'; +import { getCombinedNotesByComponent, deleteAllNotes } from './note.mjs'; +import { getNoteName } from './noteTypes.mjs'; +import { writeFile, readFile, unlink } from 'fs/promises'; +import { join as joinPath } from 'path'; +import logger from './logger.mjs'; +import { getCurrentVersion } from './helpers.mjs'; + +/** + * Helper to fetch the current notes from a file. + * + * @param {string} file + * @returns {Promise} + */ +const getCurrentNotes = async (file) => { + try { + return await readFile(file, 'utf8'); + } catch (error) { + return null; + } +} + +/** + * Update the UPGRADING.md file. + * + * @param {string} upgradeNotes + * @param {Object} options + * @param {boolean} options.deleteNotes + * @returns {Promise} + */ +const updateUpgradeNotes = async (upgradeNotes, options) => { + const fileName = 'UPGRADING.md'; + // Write the notes to a file. + logger.info(`Writing notes to ${chalk.underline(chalk.bold(fileName))}`); + // Prepend to the existing file. + const existingContent = await getCurrentNotes(fileName); + if (existingContent) { + await writeFile(fileName, getUpdatedNotes(existingContent, upgradeNotes)); + } else { + // This should not normally happen. + await writeFile(fileName, upgradeNotes); + } + + if (options.deleteNotes) { + logger.warn(`>>> Deleting all notes <<<`) + // Delete the notes. + deleteAllNotes(); + } +}; + +/** + * Create the current summary notes. + * + * @param {string} upgradeNotes + * @returns {Promise} + */ +const createCurrentSummary = async (upgradeNotes) => { + const fileName = 'UPGRADING-CURRENT.md'; + const notes = `# Moodle upgrade notes\n\n${upgradeNotes}`; + await writeFile(fileName, notes); + + logger.info(`Running upgrade notes written to ${chalk.underline(chalk.bold(fileName))}`); +}; + +/** + * Get the indexes of the lines that contain the version headings. + * + * @param {array} lines + * @returns {array} + */ +const getVersionLineIndexes = (lines) => { + const h2Indexes = []; + lines.forEach((line, index) => { + const matches = line.match(/^##\s(?.*)$/); + if (matches) { + h2Indexes.push({ + index, + line, + version: matches.groups.version, + }); + } + }); + + return h2Indexes; +}; + +/** + * Find the index of the Unreleased heading. + * + * @param {array} versionHeadings + * @returns {number} + */ +const findUnreleasedHeadingIndex = (versionHeadings) => versionHeadings.findIndex((heading) => { + if (heading.version === 'Unreleased') { + // Used if version cannot be guessed. + return true; + } + + if (heading.version.endsWith('+')) { + // Weekly release for a stable branch. + return true; + } + + if (heading.version.match(/beta|rc\d/)) { + // Beta and RC rolls are treated as weeklies. + return true; + } + + + + if (heading.version.endsWith('dev')) { + // Development version. + return true; + } + + return false; +}); + +/** + * Get the before and after content, to facilitate replacing any existing Unreleased notes. + * + * @param {array} lines + * @returns {Object} {beforeContent: string, afterContent: string} + */ +const getBeforeAndAfterContent = (lines) => { + const existingLines = lines.split('\n'); + const versionHeadings = getVersionLineIndexes(existingLines); + + if (versionHeadings.length > 0) { + const unreleasedHeadingIndex = findUnreleasedHeadingIndex(versionHeadings); + if (unreleasedHeadingIndex !== -1) { + const beforeContent = existingLines.slice(0, versionHeadings[unreleasedHeadingIndex].index).join('\n'); + if (versionHeadings.length > unreleasedHeadingIndex + 1) { + const afterContent = existingLines.slice(versionHeadings[unreleasedHeadingIndex + 1].index).join('\n'); + return { + beforeContent, + afterContent, + }; + } + return { + beforeContent, + afterContent: '', + }; + } + + return { + beforeContent: existingLines.slice(0, versionHeadings[0].index).join('\n'), + afterContent: existingLines.slice(versionHeadings[0].index).join('\n'), + }; + } + + return { + beforeContent: existingLines.join('\n'), + afterContent: '', + } +}; + +/** + * Get the notes for the component. + * + * @param {string} type + * @param {Number} headingLevel + * @returns {string} + */ +const getNotesForComponent = (types, headingLevel) => { + let upgradeNotes = ''; + Object.entries(types).forEach(([type, notes]) => { + upgradeNotes += '#'.repeat(headingLevel); + upgradeNotes += ` ${getNoteName(type)}\n\n`; + notes.forEach(({ message, issueNumber }) => { + // Split the message into lines, removing empty lines. + const messageLines = message + .split('\n') + .filter((line) => line.trim().length > 0); + + const firstLine = messageLines.shift().trim(); + upgradeNotes += `- ${firstLine}\n`; + + messageLines + .forEach((line) => { + upgradeNotes += ` ${line.trimEnd()}\n`; + }); + upgradeNotes += `\n For more information see [${issueNumber}](https://tracker.moodle.org/browse/${issueNumber})\n`; + }); + upgradeNotes += '\n'; + }); + + return upgradeNotes; +}; + +/** + * Get the updated notes mixed with existing content. + * + * @param {string} existingContent + * @param {string} upgradeNotes + */ +const getUpdatedNotes = (existingContent, upgradeNotes) => { + const { beforeContent, afterContent } = getBeforeAndAfterContent(existingContent); + const newContent = `${beforeContent}\n${upgradeNotes}\n${afterContent}` + .split('\n') + .filter((line, index, lines) => { + if (line === '' && lines[index - 1] === '') { + // Remove multiple consecutive empty lines. + return false; + } + return true; + }) + .join('\n'); + + return newContent; +}; + +/** + * Update the notes for each component. + */ +const updateComponentNotes = ( + notes, + version, + notesFileName = 'UPGRADING.md', + removeEmpty = false, +) => { + return getAllComponents().map(async (component) => { + logger.verbose(`Updating notes for ${component.name} into ${component.path}`); + const fileName = joinPath(component.path, notesFileName); + + const existingContent = await getCurrentNotes(fileName); + + if (!existingContent) { + if (!notes[component.value]) { + // No existing notes, and no new notes to add. + return; + } + } else { + if (!notes[component.value]) { + // There is existing content, but nothing to add. + if (removeEmpty) { + logger.verbose(`Removing empty notes file ${fileName}`); + await unlink(fileName); + } + return; + } + } + + const componentNotes = notes[component.value]; + let upgradeNotes = `## ${version}\n\n`; + upgradeNotes += getNotesForComponent(componentNotes, 3); + + if (existingContent) { + await writeFile(fileName, getUpdatedNotes(existingContent, upgradeNotes)); + } else { + await writeFile( + fileName, + `# ${component.name} Upgrade notes\n\n${upgradeNotes}`, + ); + } + }); +} + +/** + * Generate the upgrade notes for a new release. + * + * @param {string|undefined} version + * @param {Object} options + * @param {boolean} options.generateUpgradeNotes + * @param {boolean} options.deleteNotes + * @returns {Promise} + */ +export default async (version, options = {}) => { + const notes = await getCombinedNotesByComponent(); + + if (Object.keys(notes).length === 0) { + logger.warn('No notes to generate'); + return; + } + + if (!version) { + version = await getCurrentVersion(); + } + + // Generate the upgrade notes for this release. + // We have + // - a title with the release name + // - the change types + // - which contain the components + // - which document each changev + let upgradeNotes = `## ${version}\n\n`; + + Object.entries(notes).forEach(([component, types]) => { + upgradeNotes += `### ${component}\n\n`; + upgradeNotes += getNotesForComponent(types, 4); + }); + + await Promise.all([ + createCurrentSummary(upgradeNotes), + ...updateComponentNotes(notes, version, 'UPGRADING-CURRENT.md', true), + ]); + if (options.generateUpgradeNotes) { + await Promise.all(updateComponentNotes(notes, version)); + await updateUpgradeNotes(upgradeNotes, options); + } +}; diff --git a/.grunt/notes/src/helpers.mjs b/.grunt/notes/src/helpers.mjs new file mode 100644 index 00000000000..87d741e732a --- /dev/null +++ b/.grunt/notes/src/helpers.mjs @@ -0,0 +1,230 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +import chalk from 'chalk'; +import { isStandardComponent, isCommunityComponent, rewritePlugintypeAsSubsystem } from './components.mjs'; +import { isValidNoteName } from './noteTypes.mjs'; +import logger from './logger.mjs'; +import { readFile } from 'fs/promises'; + +/** + * Validate an issue number input + * + * @param {string} input + * @returns {string|boolean} + */ +export const validateIssueNumber = (input) => { + if (!input) { + return 'You must provide a tracker issue number'; + } + + if (input.match(/^[a-zA-Z]*-\d+$/)) { + return true; + } + + if (input.match(/^\d+$/)) { + return true; + } + + return 'The issue number was not recognised as a valid issue number'; +}; + +/** + * Format an issue number input. + * + * @param {string} input + * @returns {string} + */ +export const formatIssueNumber = (input) => { + if (input.match(/^[a-zA-Z]*-\d+$/)) { + return input; + } + + if (input.match(/^\d+$/)) { + return `MDL-${input}`; + } + + return input; +}; + +/** + * Validate a component. + * + * @param {string} input + * @returns {string|boolean} + */ +export const validateComponent = (input) => { + if (isStandardComponent(input)) { + return true; + } + + if (isCommunityComponent(input)) { + return 'Currently only core plugins are supported.'; + } + + return 'The component was not recognised as a standard component'; +}; + +export const formatComponent = (input) => { + if (rewritePlugintypeAsSubsystem(input)) { + return `core_${input}`; + } + return input; +} + +/** + * Get the initial values from the options. + * + * @param {object} options + * @returns {object} + */ +export const getInitialValues = (options) => { + const initialValues = {}; + + const type = getInitialTypeValue(options); + if (type) { + initialValues.type = type; + } + + const issueNumber = getInitialIssueValue(options); + if (issueNumber) { + initialValues.issueNumber = issueNumber; + } + + const component = getInitialComponentValue(options); + if (component) { + initialValues.components = component; + } + + const message = getInitialMessageValue(options); + if (message) { + initialValues.message = message + initialValues.addAnother = false; + } + + return initialValues; +}; + +/** + * Get the initial type value. + * + * @param {Object} options + * @returns {string|undefined} + */ +const getInitialTypeValue = (options) => { + if (!options.type) { + return; + } + + options.type = options.type.trim().toLowerCase(); + + if (isValidNoteName(options.type)) { + return options.type; + } + + logger.warn(`Note type "${chalk.underline(chalk.red(options.type))}" is not valid.`); +}; + +/** + * Get the initial issue number value. + * + * @param {Object} options + * @returns {string|undefined} + */ + +const getInitialIssueValue = (options) => { + if (!options.issue) { + return; + } + options.issue = options.issue.trim().toUpperCase(); + + const issueNumberValidated = validateIssueNumber(options.issue); + if (issueNumberValidated === true) { + const issueNumber = formatIssueNumber(options.issue); + if (issueNumber !== options.issue) { + logger.warn( + `Issue number "${chalk.underline(chalk.red(options.issue))}" was updated to ` + + `"${chalk.underline(chalk.green(issueNumber))}"` + ); + } + + return issueNumber; + } else { + logger.warn(`Issue number "${chalk.underline(chalk.red(options.issue))}" is not valid: ${issueNumberValidated}`); + } +}; + +/** + * Get the initial component value. + * + * @param {Object} options + * @returns {string|undefined} + */ +const getInitialComponentValue = (options) => { + if (!options.component) { + return; + } + + options.component = options.component.trim().toLowerCase(); + const componentValidated = validateComponent(options.component); + if (componentValidated === true) { + const component = formatComponent(options.component); + if (component !== options.component) { + logger.warn( + `Component "${chalk.underline(chalk.red(options.component))}" was updated to ` + + `"${chalk.underline(chalk.green(component))}"` + ); + } + + return component; + } else { + logger.warn(`Component "${chalk.underline(chalk.red(options.component))}" is not valid: ${componentValidated}`); + } +}; + +/** + * Get the initial message value. + * + * @param {Object} options + * @returns {string|undefined} + */ + +const getInitialMessageValue = (options) => { + if (!options.message) { + return; + } + + return options.message.trim(); +}; + +/** + * Get the current version from the project /version.php file. + * + * @returns {Promise} + */ +export const getCurrentVersion = async () => { + const versionRegex = new RegExp(/^ *\$release *= *['\"](?[^ \+]+\+?) *\(Build:.*/m); + try { + const versionFile = await readFile('version.php', 'utf8'); + const match = versionFile.match(versionRegex); + if (match) { + return match.groups.release; + } + } catch(error) { + logger.error('Unable to read the version file'); + } + + return "Unreleased"; +} diff --git a/.grunt/notes/src/logger.mjs b/.grunt/notes/src/logger.mjs new file mode 100644 index 00000000000..571cc992b33 --- /dev/null +++ b/.grunt/notes/src/logger.mjs @@ -0,0 +1,30 @@ +#!/usr/bin/env node +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +import winston from 'winston'; + +const logger = winston.createLogger({ + level: 'info', + format: winston.format.combine( + winston.format.colorize(), + winston.format.simple() + ), + transports: [ + new winston.transports.Console(), + ], +}); + +export default logger; diff --git a/.grunt/notes/src/note.mjs b/.grunt/notes/src/note.mjs new file mode 100644 index 00000000000..3aafcd1f35e --- /dev/null +++ b/.grunt/notes/src/note.mjs @@ -0,0 +1,183 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +import yaml from 'js-yaml'; +import path from 'path'; +import { writeFile, mkdir, readdir, readFile, unlink } from 'fs/promises'; +import { isValidNoteName } from './noteTypes.mjs'; + +const unreleasedPath = path.resolve('.upgradenotes'); + +/** + * Get the filename for the note. + * + * @param {string} issueNumnber The issue number + * @returns {string} + */ +const getFilename = (issueNumber) => { + const dateTimeFormat = new Intl.DateTimeFormat(undefined, { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + fractionalSecondDigits: 2, + timeZone: 'UTC', + }); + + const date = Object.fromEntries( + dateTimeFormat.formatToParts(new Date()) + .filter((p) => p.type !== 'literal') + .map((p) => ([p.type, p.value])) + ); + + const dateString = [ + date.year, + date.month, + date.day, + date.hour, + date.minute, + date.second, + date.fractionalSecond, + ].join(''); + + return `${issueNumber}-${dateString}.yml`; +}; + +/** + * Create a new note. + * + * @param {string} issueNumber + * @param {[type: string]: {message: string}} messages + * @returns {string} The path to the note on disk + */ +export const createNote = async ( + issueNumber, + messages, + notePath, +) => { + const note = { + issueNumber, + notes: {}, + }; + + messages.forEach(({components, type, message}) => { + note.notes[components] = note.notes[components] || []; + note.notes[components].push({message, type}); + }); + + if (!notePath) { + notePath = path.resolve(unreleasedPath, getFilename(issueNumber)); + } + const noteContent = yaml.dump(note); + + await mkdir(unreleasedPath, {recursive: true}); + await writeFile(notePath, noteContent); + + return notePath; +}; + +/** + * Get all unreleased notes. + * + * @returns {Promise<{issueNumber: string, components: string[], types: {[type: string]: {message: string}[]}}[]> + */ +export const getAllNotes = async () => { + const files = await readdir(unreleasedPath); + const notes = files + .filter((file) => file.endsWith('.yml')) + .map(async (file) => { + const filePath = path.resolve(unreleasedPath, file); + const fileContent = await readFile(filePath, 'utf8'); + + return { + ...yaml.load(fileContent), + filePath, + }; + }); + + return Promise.all(notes); +}; + +/** + * Get the list of notes, grouped by note type, then component. + * + * @returns {Promise<{[type: string]: {[components: string]: {message: string, issueNumber: string}[]}}>} + */ +export const getCombinedNotes = async () => { + const notes = await getAllNotes(); + const combinedNotes = {}; + + notes.forEach((note) => { + Object.entries(note.notes).forEach(([components, data]) => { + data.forEach((entry) => { + if (!isValidNoteName(entry.type)) { + throw new Error(`Invalid note type: "${entry.type}" in file ${note.filePath}`); + } + combinedNotes[entry.type] = combinedNotes[entry.type] || {}; + combinedNotes[entry.type][components] = combinedNotes[entry.type][components] || []; + combinedNotes[entry.type][components].push({message: entry.message, issueNumber: note.issueNumber}); + }); + }); + }); + + return combinedNotes; +}; + +/** + * Get the list of notes, grouped by component, then by note type. + * + * @returns {Promise<{[component: string]: {[type: string]: {message: string, issueNumber: string}[]}}>} + */ +export const getCombinedNotesByComponent = async () => { + const notes = await getAllNotes(); + const combinedNotes = {}; + + notes.forEach((note) => { + Object.entries(note.notes).forEach(([component, data]) => { + combinedNotes[component] = combinedNotes[component] || {}; + data.forEach((entry) => { + if (!isValidNoteName(entry.type)) { + throw new Error(`Invalid note type: "${entry.type}" in file ${note.filePath}`); + } + combinedNotes[component][entry.type] = combinedNotes[component][entry.type] || []; + combinedNotes[component][entry.type].push({ + component, + message: entry.message, + issueNumber: note.issueNumber, + type: entry.type, + }); + }); + }); + }); + + return combinedNotes; +}; + +/** + * Delete all unreleased notes. + * + * @returns {Promise} + */ +export const deleteAllNotes = async () => { + const files = await readdir(unreleasedPath); + return Promise.all( + files + .filter((item, index) => files.indexOf(item) === index) + .filter((file) => file.endsWith('.yml')) + .map((file) => unlink(`${unreleasedPath}/${file}`)) + ); +}; diff --git a/.grunt/notes/src/noteTypes.mjs b/.grunt/notes/src/noteTypes.mjs new file mode 100644 index 00000000000..eaa1834271c --- /dev/null +++ b/.grunt/notes/src/noteTypes.mjs @@ -0,0 +1,45 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +const noteTypes = { + 'improved': 'Added', + 'removed': 'Removed', + 'changed': 'Changed', + 'deprecated': 'Deprecated', + 'fixed': 'Fixed', +}; + +/** + * Get the note names. + * + * @returns {string[]} + */ +export const getNoteNames = () => Object.keys(noteTypes); + +/** + * Get the human-readable note name. + * + * @param {string} type + * @returns {string} + */ +export const getNoteName = (type) => noteTypes[type]; + +/** + * Whether the note name is valid. + * + * @param {string} type + * @returns {boolean} + */ +export const isValidNoteName = (type) => noteTypes[type] !== undefined; diff --git a/.grunt/notes/src/prompts.mjs b/.grunt/notes/src/prompts.mjs new file mode 100644 index 00000000000..8b95ed1724d --- /dev/null +++ b/.grunt/notes/src/prompts.mjs @@ -0,0 +1,153 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +import inquirer from 'inquirer'; +import SearchList from 'inquirer-search-list'; + +import { getNoteNames } from './noteTypes.mjs'; +import { getAllComponents } from './components.mjs'; +import { + formatComponent, + formatIssueNumber, + validateComponent, + validateIssueNumber, + } from './helpers.mjs'; + +/** + * A Search List which accepts an initial value. + */ +class SearchListWithInitialValue extends SearchList { + constructor(options, ...args) { + super(options, ...args); + + if (options.default) { + const pointer = this.filterList.findIndex((item) => { + return item.value === options.default; + }); + if (pointer > -1) { + this.pointer = pointer; + } + } + } +} + +inquirer.registerPrompt('search-list', SearchListWithInitialValue); + +/** + * Get the issue type prompt. + * + * @param {string} defaultData The initially selected value + * @returns {Object} + */ +export const getTypePrompt = (defaultData) => ({ + default: defaultData, + type: 'search-list', + message: 'Type of change', + name: 'type', + choices: getNoteNames(), + validate: (selection) => { + if (selection.length < 1) { + return 'You must select at least one type of change'; + } + + return true; + }, +}); + +/** + * Get the component prompt. + * + * @param {string} [defaultValue='core'] The initally selected value. + * @returns + */ +export const getComponentsPrompt = (defaultValue) => { + if (!defaultValue ) { + defaultValue = 'core'; + } + + return { + choices: getAllComponents(), + default: defaultValue, + type: 'search-list', + message: 'Component', + name: 'components', + validate: validateComponent, + filter: formatComponent, + }; +}; + +/** + * Get the issue number prompt as an inline input. + * + * @param {string} defaultData + * @returns {object} + */ +export const getIssuePrompt = (defaultData) => ({ + default: defaultData, + type: 'input', + message: 'Tracker issue number', + name: 'issueNumber', + validate: validateIssueNumber, + filter: formatIssueNumber, +}); + +/** + * Get a message prompt. + * + * @param {string} defaultData + * @returns + */ +export const getMessagePromptEditor = (defaultData) => ({ + default: defaultData, + type: process.stdin.isTTY ? 'editor' : 'input', + postfix: '.md', + message: 'Message', + name: 'message', + waitUserInput: false, + validate: (input) => { + if (!input) { + return 'You must provide a message'; + } + return true; + }, + // Remove any trailing whitespace. + filter: (input) => input.split('\n').map((line) => line.trimEnd()).join('\n'), +}); + +/** + * Get a message prompt. + * + * @param {string} defaultData + * @returns + */ +export const getMessagePromptInput = (defaultData) => ({ + default: defaultData, + type: 'input', + message: 'Message (leave empty to use editor)', + name: 'message', + filter: (input) => input.trim(), +}); + +/** + * Get a prompt to ask the user if they wish to add another entry. + * + * @returns {Object} + */ +export const getAddAnotherPrompt = () => ({ + type: 'confirm', + message: 'Do you want to add another note?', + default: false, + name: 'addAnother', +}); diff --git a/.grunt/upgradenotes.mjs b/.grunt/upgradenotes.mjs new file mode 100755 index 00000000000..c274049bfaf --- /dev/null +++ b/.grunt/upgradenotes.mjs @@ -0,0 +1,103 @@ +#!/usr/bin/env node +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +import { Argument, Option, program } from 'commander'; +import chalk from 'chalk'; + +import { getNoteNames } from './notes/src/noteTypes.mjs'; +import createAction from './notes/src/create.mjs'; +import generateAction from './notes/src/generate.mjs'; +import logger from './notes/src/logger.mjs'; + +console.log(` +${chalk.bold(chalk.underline(chalk.green('Moodle Upgrade Notes Generator')))} + +This tool is used to generate the upgrade notes for changes you make in Moodle. + +Please remember that the intended audience of these changes is +${chalk.italic('plugin developers')} who need to know how to update their plugins +for a new Moodle version. + +Upgrade notes should not be used to document changes for site administrators, or +for internal API changes which are not expected to be used outside of the +relevant component. +`) + +program.configureHelp({ + helpWidth: 100, +}); + +program.on('option:verbose', () => { + logger.level = 'verbose'; +}); + +program.addOption( + new Option( + '-v, --verbose', + 'Output more information during the generation process', + ) + .default(false) +); + +// Define the command line options. +program + .command('create') + .summary('Generate a new upgrade note') + .addOption( + new Option('-t, --type ', `The type of change to document. Valid types are: ${getNoteNames().join(', ')}`) + ) + .addOption(new Option('-i, --issue ', 'The tracker issue number')) + .addOption(new Option('-c, --component ', 'The component to write a note for')) + .addOption(new Option( + '-m, --message ', + 'The message to use for the upgrade note', + )) + .action((options) => createAction(options)); + +program + .command('summary') + .summary('Generate a local copy of the upgrade notes summary') + .addArgument( + new Argument('[version]', 'The Moodle version to create the summary notes for') + ) + .action((version) => generateAction(version)); + +program + .command('release') + .summary('Generate the markdown copies of the upgrade notes for a Moodle release') + .addArgument( + new Argument('[version]', 'The Moodle version to create the release notes for') + ) + .addOption( + new Option( + '--generate-upgrade-notes', + 'Generate the UPGRADING.md notes for the release. ' + + 'Note: This option is intended for use by the release manager when generating the upgrade notes.', + ) + .default(true) + ) + .addOption( + new Option( + '-d, --delete-notes', + 'Delete the notes after generating the UPGRADING.md notes for the release. ' + + 'Note: This option is intended for use by the release manager when generating the upgrade notes.' + + 'This option has no effect unless --generate-upgrade-notes is also set.' + ) + .default(false) + ) + .action((version, options) => generateAction(version, options)); + +program.parse(); diff --git a/.upgradenotes/.gitkeep b/.upgradenotes/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/UPGRADING.md b/UPGRADING.md new file mode 100644 index 00000000000..dbad8e733de --- /dev/null +++ b/UPGRADING.md @@ -0,0 +1,7 @@ +# Moodle Upgrade notes + +This file contains important information for developers on changes to the Moodle codebase. + +More detailed information on key changes can be found in the [Developer update notes](https://moodledev.io/docs/devupdate) for your version of Moodle. + +The format of this change log follows the advice given at [Keep a CHANGELOG](https://keepachangelog.com). diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index b3d9fc361d8..50c070eea3e 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -16,6 +16,8 @@ "babel-plugin-system-import-transformer": "^4.0.0", "babel-plugin-transform-es2015-modules-amd-lazy": "2.0.1", "babel-preset-minify": "0.5.1", + "chalk": "^5.3.0", + "commander": "^12.0.0", "cross-env": "^7.0.3", "docdash": "^2.0.2", "eslint": "^8.56.0", @@ -33,6 +35,8 @@ "grunt-stylelint": "^0.19.0", "hugo-bin": "^0.80.2", "hugo-lunr-indexer": "^1.1.3", + "inquirer": "^9.2.15", + "inquirer-search-list": "^1.2.6", "jsdoc": "^4.0.2", "jsdoc-to-markdown": "^8.0.0", "jshint": "^2.13.4", @@ -45,6 +49,7 @@ "shifter": "https://github.com/andrewnicols/shifter/archive/v1.2.0_moodle-0.tar.gz", "stylelint": "^15.11.0", "stylelint-csstree-validator": "^3.0.0", + "winston": "^3.13.0", "xpath": "0.0.32" }, "engines": { @@ -568,6 +573,44 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/parser": { "version": "7.23.9", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", @@ -1713,6 +1756,15 @@ "node": ">=6.9.0" } }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@csstools/css-parser-algorithms": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.5.0.tgz", @@ -1799,6 +1851,17 @@ "postcss-selector-parser": "^6.0.13" } }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dev": true, + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "node_modules/@es-joy/jsdoccomment": { "version": "0.41.0", "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.41.0.tgz", @@ -2068,6 +2131,18 @@ "node": ">=v12.0.0" } }, + "node_modules/@ljharb/through": { + "version": "2.3.13", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz", + "integrity": "sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -2241,6 +2316,12 @@ "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", "dev": true }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "dev": true + }, "node_modules/@types/uuid": { "version": "3.4.10", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.10.tgz", @@ -2350,6 +2431,33 @@ "node": ">=6" } }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -3194,13 +3302,19 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3283,42 +3397,22 @@ } }, "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, "engines": { - "node": ">=4" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true }, "node_modules/chokidar": { "version": "3.5.3", @@ -3386,6 +3480,39 @@ "node": ">=0.2.5" } }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, "node_modules/cli/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -3418,6 +3545,15 @@ "node": "*" } }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, "node_modules/clone-response": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", @@ -3440,6 +3576,16 @@ "node": ">=0.10.0" } }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -3455,6 +3601,16 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -3470,6 +3626,16 @@ "node": ">=0.1.90" } }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dev": true, + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, "node_modules/command-line-args": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", @@ -3559,12 +3725,12 @@ } }, "node_modules/commander": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.0.0.tgz", - "integrity": "sha512-JrDGPAKjMGSP1G0DUoaceEJ3DZgAfr/q6X7FVk4+U5KxUSKviYGM2k6zWkfyyBHy5rAtzgYJFa1ro2O9PtoxwQ==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz", + "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==", "dev": true, "engines": { - "node": ">= 6" + "node": ">=18" } }, "node_modules/comment-parser": { @@ -4197,6 +4363,35 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -4371,6 +4566,12 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "dev": true + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -4434,16 +4635,25 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-abstract/node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, "node_modules/es-to-primitive": { @@ -5211,6 +5421,32 @@ "node": ">=0.10.0" } }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/external-editor/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5293,6 +5529,27 @@ "pend": "~1.2.0" } }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "dev": true + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/file-entry-cache": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-7.0.2.tgz", @@ -5584,6 +5841,12 @@ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "dev": true + }, "node_modules/for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -5724,10 +5987,22 @@ "dev": true }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fuzzy": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/fuzzy/-/fuzzy-0.1.3.tgz", + "integrity": "sha512-/gZffu4ykarLrCiP3Ygsa86UAo1E5vEVlvTrpkKywXSbP9Xhln3oSp9QSV57gEq3JFFpGJ4GZ+5zdEp3FcUh4w==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } }, "node_modules/gaze": { "version": "1.1.3", @@ -6018,14 +6293,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6096,6 +6376,15 @@ "node": ">=10.0.0" } }, + "node_modules/gherkin-lint/node_modules/commander": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.0.0.tgz", + "integrity": "sha512-JrDGPAKjMGSP1G0DUoaceEJ3DZgAfr/q6X7FVk4+U5KxUSKviYGM2k6zWkfyyBHy5rAtzgYJFa1ro2O9PtoxwQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/gherkin-lint/node_modules/glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -6296,6 +6585,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/got": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz", @@ -6826,12 +7127,36 @@ "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { "node": ">=4" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbol-support-x": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", @@ -6842,9 +7167,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, "engines": { "node": ">= 0.4" @@ -6865,6 +7190,18 @@ "node": "*" } }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hawk": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.1.1.tgz", @@ -7153,9 +7490,9 @@ } }, "node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "node_modules/ini": { @@ -7164,6 +7501,328 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "node_modules/inquirer": { + "version": "9.2.15", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.15.tgz", + "integrity": "sha512-vI2w4zl/mDluHt9YEQ/543VTCwPKWiHzKtm9dM2V0NdFcqEexDAjUHzO1oA60HRNaVifGXXM1tRRNluLVHa0Kg==", + "dev": true, + "dependencies": { + "@ljharb/through": "^2.3.12", + "ansi-escapes": "^4.3.2", + "chalk": "^5.3.0", + "cli-cursor": "^3.1.0", + "cli-width": "^4.1.0", + "external-editor": "^3.1.0", + "figures": "^3.2.0", + "lodash": "^4.17.21", + "mute-stream": "1.0.0", + "ora": "^5.4.1", + "run-async": "^3.0.0", + "rxjs": "^7.8.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/inquirer-search-list": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/inquirer-search-list/-/inquirer-search-list-1.2.6.tgz", + "integrity": "sha512-C4pKSW7FOYnkAloH8rB4FiM91H1v08QFZZJh6KRt//bMfdDBIhgdX8wjHvrVH2bu5oIo6wYqGpzSBxkeClPxew==", + "dev": true, + "dependencies": { + "chalk": "^2.3.0", + "figures": "^2.0.0", + "fuzzy": "^0.1.3", + "inquirer": "^3.3.0" + } + }, + "node_modules/inquirer-search-list/node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-search-list/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-search-list/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-search-list/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-search-list/node_modules/chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha512-j/Toj7f1z98Hh2cYo2BVr85EpIRWqUi7rtRSGxh/cqUjqrnJe9l9UE7IUGd2vQ2p+kSHLkSzObQPZPLUC6TQwg==", + "dev": true + }, + "node_modules/inquirer-search-list/node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "dev": true, + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-search-list/node_modules/cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "node_modules/inquirer-search-list/node_modules/external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "dev": true, + "dependencies": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/inquirer-search-list/node_modules/figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-search-list/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inquirer-search-list/node_modules/inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.0.4", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rx-lite": "^4.0.8", + "rx-lite-aggregates": "^4.0.8", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + } + }, + "node_modules/inquirer-search-list/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-search-list/node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-search-list/node_modules/mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", + "dev": true + }, + "node_modules/inquirer-search-list/node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-search-list/node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "dev": true, + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-search-list/node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/inquirer-search-list/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-search-list/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer-search-list/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/inquirer/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/inquirer/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/interpret": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", @@ -7328,6 +7987,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-natural-number": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", @@ -7422,18 +8090,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-regex/node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-relative": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", @@ -7491,18 +8147,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-symbol/node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", @@ -7515,6 +8159,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -8061,6 +8717,12 @@ "node": "*" } }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "dev": true + }, "node_modules/less": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/less/-/less-1.7.5.tgz", @@ -8426,6 +9088,88 @@ "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/logform": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", + "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", + "dev": true, + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", @@ -8715,6 +9459,15 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/mimic-response": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", @@ -8798,6 +9551,15 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -8981,6 +9743,20 @@ "node": ">=4" } }, + "node_modules/npm-run-all/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/npm-run-all/node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -9053,6 +9829,18 @@ "node": ">=4" } }, + "node_modules/npm-run-all/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -9174,6 +9962,30 @@ "wrappy": "1" } }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dev": true, + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optimist": { "version": "0.3.7", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", @@ -9209,6 +10021,103 @@ "node": ">= 0.8.0" } }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ora/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ora/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/ora/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/os-filter-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz", @@ -10259,6 +11168,19 @@ "lowercase-keys": "^1.0.0" } }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -10346,6 +11268,15 @@ "rollup": "^2.0.0" } }, + "node_modules/run-async": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -10369,6 +11300,30 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha512-Cun9QucwK6MIrp3mry/Y7hqD1oFqTYLQ4pGxaHTjIdaFDWRGGLikqp6u8LcWJnzpoALg9hap+JGk8sFIUuEGNA==", + "dev": true + }, + "node_modules/rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha512-3xPNZGW93oCjiO7PtKxRK6iOVYBWBvtf9QHDfU23Oc+dLIQmAV//UnyXV/yihv81VS/UqoQPk4NegS8EFi55Hg==", + "dev": true, + "dependencies": { + "rx-lite": "*" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -10381,6 +11336,15 @@ "integrity": "sha1-PnZyPjjf3aE8mx0poeB//uSzC1c=", "dev": true }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -10501,6 +11465,23 @@ "randombytes": "^2.1.0" } }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -10591,6 +11572,21 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -10794,6 +11790,15 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/stream-connect": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz", @@ -11418,6 +12423,12 @@ "node": ">=4" } }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -11469,6 +12480,18 @@ "ms": "^2.1.1" } }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/to-buffer": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", @@ -11540,6 +12563,21 @@ "node": ">=0.10.0" } }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "dev": true, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -11621,18 +12659,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/unbox-primitive/node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/unbzip2-stream": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", @@ -11806,6 +12832,15 @@ "node >=0.1.95" ] }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, "node_modules/websocket-driver": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", @@ -11866,6 +12901,82 @@ "node": ">= 0.8.0" } }, + "node_modules/winston": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", + "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", + "dev": true, + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz", + "integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==", + "dev": true, + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/winston/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/winston/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", diff --git a/package.json b/package.json index 35410165952..5521e481e54 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,9 @@ "name": "Moodle", "private": true, "description": "Moodle", + "scripts": { + "upgradenote": "node .grunt/upgradenotes.mjs create" + }, "devDependencies": { "@babel/core": "7.17.5", "@babel/eslint-parser": "^7.21.3", @@ -13,6 +16,8 @@ "babel-plugin-system-import-transformer": "^4.0.0", "babel-plugin-transform-es2015-modules-amd-lazy": "2.0.1", "babel-preset-minify": "0.5.1", + "chalk": "^5.3.0", + "commander": "^12.0.0", "cross-env": "^7.0.3", "docdash": "^2.0.2", "eslint": "^8.56.0", @@ -30,6 +35,8 @@ "grunt-stylelint": "^0.19.0", "hugo-bin": "^0.80.2", "hugo-lunr-indexer": "^1.1.3", + "inquirer": "^9.2.15", + "inquirer-search-list": "^1.2.6", "jsdoc": "^4.0.2", "jsdoc-to-markdown": "^8.0.0", "jshint": "^2.13.4", @@ -42,6 +49,7 @@ "shifter": "https://github.com/andrewnicols/shifter/archive/v1.2.0_moodle-0.tar.gz", "stylelint": "^15.11.0", "stylelint-csstree-validator": "^3.0.0", + "winston": "^3.13.0", "xpath": "0.0.32" }, "engines": {