diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 7346f90..5487d1b 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -2,8 +2,8 @@ `mediumroast_js` is a Node.js Javascript SDK and CLI for Mediumroast for GitHub. In roughly includes the following high level features. 1. A wrapper build atop the GitHub API to make it easier to work with the specifics for Mediumroast for GitHub this is included in `src/api` as `authorize.js`, `github.js`, and `gitHubServer.js`. -2. CLI scaffolding for companies, interactions, users, actions, and storage consumption found in `src/cli`. -3. Report scaffolding for companies and interactions found in `src/report`. +2. CLI scaffolding for studies, companies, interactions, users, actions, and storage consumption found in `src/cli`. +3. Report scaffolding for studies, companies and interactions found in `src/report`. 4. Implementations of CLIs for companies, interactions, users, actions, and storage consumption found in `cli/`. 5. GitHub Actions and Workflows to provide core reporting and repository cleanliness found in `cli/actions`. @@ -12,9 +12,6 @@ Since we're using this SDK, and the Python version [mediumroast_py](https://gith ## Developing for Mediumroast for GitHub If you're interested in developing for the SDK or please follow the steps below to get started. -### Job jar -We will be adding a job jar for contributors to the project in the near future. The job jar will contain a list of tasks that need to be completed to move the project forward. If you're interested in contributing to the project please check back soon for the job jar. - ### Cloning the repository Assuming `git` is installed and your credentials are set up to talk to the mediumroast.io set of repositories it should be possible to do the following as a user on the system: 1. `mkdir ~/dev;cd ~/dev` @@ -32,6 +29,14 @@ For developers the following steps are suggested to verify the local changes are 3. Enter the `cli/` directory and run the CLI you've made changes to, for example let's assume there are changes made to `cli/mrcli-company.js` you'd run `./mrcli.js company` to test your changes. 4. If you're creating a new CLI please take a look at one of the existing CLIs in the `cli/` directory to see how they are structured and how they interact with the SDK. A good example is `cli/mrcli-company.js` which manages companies. Since the `.gitignore` file is set to ignore file names like `test.js`, `test.json`, `foo.txt`, etc. you can create these files to test your changes in the `cli/` directory. +### Job jar +The job jar is a list of tasks that need to be completed to improve the SDK and CLI. + +1. Confirm all CLIs are working as expected on WinOS. +2. Enhance Studies reporting to cover all data contained within a study. +3. Review and refactor general output functions for all CLIs. +4. Consistently implement error handling and messages in all parts of the SDK and CLI. + ## Developing with Mediumroast for GitHub Follow the steps needed to install the SDK/CLI which is documented in the [main README](https://github.com/mediumroast/mediumroast_js/README.md). Once installed the SDK/CLI is available for use, and the documentation for developers is available [here](https://mediumroast.github.io/mediumroast_js/). Additionally, the CLIs in `cli/` are available as examples of constructing an application with the SDK. diff --git a/README.md b/README.md index a7e7833..17fc288 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ ## Welcome Mediumroast for GitHub -Products organizations must build robust product plans from competitive and customer interactions everyone can see, use, and reference. Therefore, Mediumroast for GitHub intends to help Products oranizations construct an active interactions repository close to the action of development and issue management in GitHub. +Products organizations must build evidence based product plans from competitive and customer interactions. Using Artificial Intelligence and Machine Learning, Mediumroast for GitHub realizes an active product planning repository for Product Managers and Developers everyone can see, use, and reference. The repository is built on GitHub and is accessible via Command Line Interface (CLI) and a Software Development Kit (SDK). This CLI and SDK are built on Node.js and is available for Linux and MacOS operating systems. -### Notices -- A new version of the CLI is available and the major focus of this version is to add in Competitive Similarity Analysis, Interaction summarization and Interaction Proto-requirements discovery. -- You can review the [GitHub Page Version](https://mediumroast.github.io/mediumroast_js/) rather than the repository version of this documentation, but the screencasts of several of the CLI tutorials will not display. +A [GitHub Page Version](https://mediumroast.github.io/mediumroast_js/) of this content is also available, but the screencasts and videos won't display. ## Installation and configuration Mediumroast for GitHub includes a [GitHub Application](https://github.com/marketplace/mediumroast-for-github), a Command Line Interface, and a Software Development Kit. The following steps show you how to install the App and the CLI with SDK. @@ -72,7 +70,15 @@ If you're interested in contributing to the Mediumroast for GitHub project, plea ## Issues If you detect a problem or want to suggest an improvement open an [issue](https://github.com/mediumroast/mediumroast_js/issues) and we will work with you to resolve or respond. +## Support +If you need support or would like to have Mediumroast run the Caffeine Machine Intelligence service on your repository please contact the Mediumroast team via [Discord](https://discord.gg/ebM4Cf8meK) or email us at [hello@mediumroast.io], (mailto:hello@mediumroast.io). +### Release notes +#### Version 0.8.06.01 +- The latest version of the SDK and CLI package now includes an initial implementation of Studies with the Foundation study. +- The SDK and CLI may run on for Windows OS, but it has not been tested. If you choose to install and run into problems please open an issue and we will work with you to resolve the matter. +- `cli-table` has been replaced by `cli-table3` to ensure that CLI tabular outputs are on a stable and maintained package. +- The `xlsx` package has been removed and replaced with `exceljs` for improved output to Excel files, and due to the security vulnerability in the `xlsx` package. diff --git a/cli/Company.md b/cli/Company.md index 7f1cef3..8de9ca7 100644 --- a/cli/Company.md +++ b/cli/Company.md @@ -1,8 +1,9 @@ ## Companies Company objects are central to Mediumroast for GitHub. Interactions and in the future Studies rely on Companies to function. After setup is run, via `mrcli setup`, two companies are present to work with. Additional Companies can be added, updated, or removed; essentially, `company` is an `mrcli` sub-command that affords users Create, Read, Update and Delete capabilities. Each of the major functions for `mrcli company` are described in this document. -### Notice -Some of the command line options and switches may not yet be implemented; therefore, if a switch or option is not yet implemented the CLI will inform the user and then exit. +### Notice(s) +- Some command line options and switches may not yet be implemented; therefore, if a switch or option is not yet implemented the CLI will inform the user and then exit. +- Similarity comparisons of Companies requires a run of the Mediumroast for GitHub Caffeine Machine Intelligence service. If you're interested in running the Caffeine Machine Intelligence service please contact the Mediumroast for GitHub team via [Discord](https://discord.gg/ebM4Cf8meK) or email us at [hello@mediumroast.io], (mailto:hello@mediumroast.io). ## Help Prints the usage for the `company` sub-command and exits. @@ -36,12 +37,12 @@ Output a list of company objects in properly formatted JSON to STDOUT which can #### Screenshot with output companies_json -### List all company objects and output to a CSV or XLSX -Company data can be output in either a CSV or XLSX files to enable consumption in common tools like Microsoft Excel. The resulting files will be stored in $HOME/Documents directory as `Mr_Companies.csv` or `Mr_Companies.xlsx` depending on your intended output. +### List all company objects and output to a CSV +Company data can be output in CSV files to enable consumption in common tools like Microsoft Excel or Apple Numbers. The resulting files will be stored in $HOME/Documents directory as `Mr_Companies.csv`. #### Command(s) run - `mrcli c --output=csv` -- `mrcli c --output=xls` +- `mrcli company --output=csv` #### Screenshot of commands being run companies_csv diff --git a/cli/Demo.md b/cli/Demo.md index 2024d92..2b73d3d 100644 --- a/cli/Demo.md +++ b/cli/Demo.md @@ -30,6 +30,4 @@ The following limitations are in place for the demo repository: 1. The demo repository is a clone of the main repository and is updated weekly. 2. The demo repository does not have the actions that automatically generate reports and clean up branches, which means that `mrcli actions` will not work as expected. -3. The demo repository is a shared read-only repository, meaning you can't push changes to it. If you need to test changes, please use your own repository. - -If you have any questions or need help, please open an [issue](https://github.com/mediumroast/mediumroast_js/issues) \ No newline at end of file +3. The demo repository is a shared read-only repository, meaning you can't push changes to it. If you need to test changes, please use your own repository. \ No newline at end of file diff --git a/cli/Interaction.md b/cli/Interaction.md index f32b09b..6e32c2d 100644 --- a/cli/Interaction.md +++ b/cli/Interaction.md @@ -1,8 +1,9 @@ ## Interactions After running `mrcli setup` you can start adding Interactions. An Interaction can be as simple as an email thread between an account team and a customer, a detailed customer interview or even documentation about a competitor. Additional Interactions can be added, updated, or removed; essentially, `interaction` is an `mrcli` sub-command that affords users Create, Read, Update and Delete capabilities. Each of the major functions for `mrcli interaction` are described in this document. -### Notice -Some of the command line options and switches may not yet be implemented; therefore, if a switch or option is not yet implemented the CLI will inform the user and then exit. +### Notice(s) +- Some command line options and switches may not yet be implemented; therefore, if a switch or option is not yet implemented the CLI will inform the user and then exit. +- Summarization of Interaction contents and generating proto-requirements plus competitive insights requires a run of the Mediumroast for GitHub Caffeine Machine Intelligence service. If you're interested in running the Caffeine Machine Intelligence service please contact the Mediumroast for GitHub team via [Discord](https://discord.gg/ebM4Cf8meK) or email us at [hello@mediumroast.io], (mailto:hello@mediumroast.io). ## Help Prints the usage for the `interaction` sub-command and exits. @@ -40,12 +41,12 @@ Output a list of company objects in properly formatted JSON to STDOUT which can interactions_json -### List all interaction objects and output to a CSV or XLSX -Interaction data can be output in either a CSV or XLSX files to enable consumption in common tools like Microsoft Excel. The resulting files will be stored in $HOME/Documents directory as `Mr_Interactions.csv` or `Mr_Interactions.xlsx` depending on your intended output. +### List all interaction objects and output to a CSV +Interaction data can be output in CSV files to enable consumption in common tools like Microsoft Excel or Apple Numbers. The resulting files will be stored in $HOME/Documents directory as `Mr_Interactions.csv`. #### Command(s) run - `mrcli i --output=csv` -- `mrcli i --output=xls` +- `mrcli interaction --output=csv` #### Screenshot of commands being run interactions_csv diff --git a/cli/README.md b/cli/README.md index 4e0fac2..7f42dcf 100644 --- a/cli/README.md +++ b/cli/README.md @@ -71,7 +71,7 @@ Is replaced by `storage` and `actions` commands in **Mediumroast for GitHub**. I --- -[[Company Subcommand](https://github.com/mediumroast/mediumroast_js/blob/main/cli/Company.md)] | [[Interaction Subcommand](https://github.com/mediumroast/mediumroast_js/blob/main/cli/Interaction.md)] +[[Company Subcommand](https://github.com/mediumroast/mediumroast_js/blob/main/cli/Company.md)] | [[Interaction Subcommand](https://github.com/mediumroast/mediumroast_js/blob/main/cli/Interaction.md) | [[Studies Subcommand](https://github.com/mediumroast/mediumroast_js/blob/main/cli/Study.md)] diff --git a/cli/Study.md b/cli/Study.md new file mode 100644 index 0000000..2ce2526 --- /dev/null +++ b/cli/Study.md @@ -0,0 +1,102 @@ +## Studies +Study objects are central to Mediumroast for GitHub. Studies rely on Interactions and Companies to function. At this time only the **Foundation study** is implemented; a future update will add support for user defined studies. To enable the Foundation study run `mrcli study --init_foundation`, which will create the basic structures of the study in the repository. Each of the major functions for `mrcli study` are described in this document. + +### Notice(s) +- Some of the command line options and switches may not yet be implemented; therefore, if a switch or option is not yet implemented the CLI will inform the user and then exit. +- Study deletion is not yet implemented and not present. +- The only update possible to the Foundation study is to reset its status to cause Caffeine to reprocess the study. +- Populating the Foundation study requires a run of the Mediumroast for GitHub Caffeine Machine Intelligence service. If you're interested in running the Caffeine Machine Intelligence service please contact the Mediumroast for GitHub team via [Discord](https://discord.gg/ebM4Cf8meK) or email us at [hello@mediumroast.io], (mailto:hello@mediumroast.io). + + +## Help +Prints the usage for the `study` sub-command and exits. + +### Command(s) run +- `mrcli study --help` + +### Screenshot with output + + +## List study objects +Print out one or more Studies to the command line or an alternative output mechanism like a CSV file. Filtering can be applied to find Studies with specific attributes. + +### List all study objects in a table format +This is the default output when running `mrcli s` or `mrcli study` which prints a text table to STDOUT. + +#### Command(s) run +- `mrcli s` +- `mrcli study` + +#### Screenshot with output + + +### List all study objects in JSON format +Output a list of study objects in properly formatted JSON to STDOUT which can be viewed, redirected to a file, or piped to another command. + +#### Command(s) run +- `mrcli s --output=json` +- `mrcli study --output=json` + +#### Screenshot with output + + +### List all study objects and output to a CSV +Study data can be output in a CSV file to enable consumption in common tools like Microsoft Excel. The resulting file will be stored in $HOME/Documents directory as `Mr_Studies.csv`. + +#### Command(s) run +- `mrcli s --output=csv` +- `mrcli study --output=csv` + +#### Screenshot of commands being run + + +### Screenshot of CSV imported into MacOS numbers + + +### Filter study outputs +The CLI offers the ability to filter outputs by almost any study attribute. This is manifest by two switches on the study sub-command one specific to finding studies by name, `--find_by_name` and the other by an arbitrary attribute, `--find_by_x`. Note all output format options, like JSON, CSV, etc., are available when the outputs are filtered. Finally, only exact matches are supported, meaning if you want to search for a study using any attribute you have to fully provide the attribute's value (i.e., "Med" would not match "Mediumroast Study", but "Mediumroast Study" would). + +#### Filter a study by name +To zero in on a specific study using the find by name switch is provided. + +#### Command(s) run +- `mrcli s --find_by_name="Foundation"` + +#### Screenshot with output + + +#### Filter a study by attribute +Find a specific study by a particular attribute in the example below the switch filters on the attribute `status`. + +#### Command(s) run +- `mrcli s --find_by_x='{"status": "0"}'` + +#### Screenshot with output + + +## Initialize the Foundation study +Initializes the Foundation study with core attributes and data. This is the first step in setting up the Foundation study. + +### Command(s) run +- `mrcli s --init_foundation` + +### Screenshot with output + + +## Report on a study +Produce a report on a study. The report provides the most important insights per company in Microsoft Excel format as of the current release. The report is stored in the `$HOME/Documents` directory as `.xlsx`. + +### Command(s) run +- `mrcli s --report="Foundation"` + +### Screenshot of the report + + +## Reset the status of a study +Reset the status of a study will cause Caffeine to reprocess the study on its next run. This is useful when the study has been updated and needs to be reprocessed. + +### Command(s) run +- `mrcli s --reset_by_name="Foundation"` + +### Screenshot with output + diff --git a/cli/actions/actions/basic-reporting/companies.js b/cli/actions/actions/basic-reporting/companies.js index b7724e4..bc3419b 100644 --- a/cli/actions/actions/basic-reporting/companies.js +++ b/cli/actions/actions/basic-reporting/companies.js @@ -31,26 +31,52 @@ function createCompaniesMap (companies) { return mrMarkdownBuilder.cr() + map } -function createCompaniesReport (companies) { - let readme = `[${mrMarkdownBuilder.link('Back to main README', '../README.md')}]\n` - readme += mrMarkdownBuilder.hr() - readme += mrMarkdownBuilder.h1('Introduction') - readme += `There are currently \`${companies.length}\` companies in the repository. The table below lists all available companies and some of their firmographics. Click on the company name to view the company's profile. Below the table is a map of all companies in the repository. Click on a company's marker to view additional company information in context.` - readme += mrMarkdownBuilder.h1('Table of Companies') +function createTableOfCompanies (companies) { // Create the table header - const tableHeader = mrMarkdownBuilder.tableHeader(['Company Name', 'Company Type', 'Company Role', 'Company Region']) + const tableHeader = mrMarkdownBuilder.tableHeader(['Company Name', 'Company Type', 'Company Role', 'Company Region', 'Total Interactions']) + // Count the total interactions for each company by looking at the number of keys in linked_interactions + companies.forEach((company) => { + company.total_interactions = Object.keys(company.linked_interactions).length + }) + // Create the table rows const tableRows = companies.map((company) => { const companyRow = [ mrMarkdownBuilder.link(company.name, `./${encodeURI(company.name.replace(/[\s,.\?!]/g, ''))}.md`), company.company_type, company.role, - company.region + company.region, + company.total_interactions ] return companyRow }) // Create the table const companyTable = tableHeader + "\n" + mrMarkdownBuilder.tableRows(tableRows) + // Return the table + return companyTable +} + +function createCompaniesReport (companies) { + let readme = `[${mrMarkdownBuilder.link('Back to main README', '../README.md')}]\n` + readme += mrMarkdownBuilder.hr() + readme += mrMarkdownBuilder.h1('Introduction') + readme += `There are currently \`${companies.length}\` companies in the repository. The table below lists all available companies and some of their firmographics. Click on the company name to view the company's profile. Below the table is a map of all companies in the repository. Click on a company's marker to view additional company information in context.` + readme += mrMarkdownBuilder.h1('Table of Companies') + // // Create the table header + // const tableHeader = mrMarkdownBuilder.tableHeader(['Company Name', 'Company Type', 'Company Role', 'Company Region']) + // // Create the table rows + // const tableRows = companies.map((company) => { + // const companyRow = [ + // mrMarkdownBuilder.link(company.name, `./${encodeURI(company.name.replace(/[\s,.\?!]/g, ''))}.md`), + // company.company_type, + // company.role, + // company.region + // ] + // return companyRow + // }) + // // Create the table + // const companyTable = tableHeader + "\n" + mrMarkdownBuilder.tableRows(tableRows) + const companyTable = createTableOfCompanies(companies) // Create the README.md file readme += companyTable // Add a line break @@ -63,5 +89,6 @@ function createCompaniesReport (companies) { } module.exports = { - createCompaniesReport + createCompaniesReport, + createTableOfCompanies } \ No newline at end of file diff --git a/cli/actions/actions/basic-reporting/company.js b/cli/actions/actions/basic-reporting/company.js index d1dbce8..b3e8978 100644 --- a/cli/actions/actions/basic-reporting/company.js +++ b/cli/actions/actions/basic-reporting/company.js @@ -217,7 +217,13 @@ async function createSimilarCompanies(similarCompanies, companies, interactions) const competitors = getCompetitors(similarCompanies, companies, interactions) const companyObject = competitors.mostSimilar const mostSimilarCompany = competitors.mostSimilar.name - // Create the most similar company section + + + // Create a list of similar companies to process + // const mostSimilarCompany = Object.keys(similarCompanies).reduce((a, b) => similarCompanies[a] > similarCompanies[b] ? a : b) + // Append the most similar company to the list from the companies array + // const companyObject = companies.find((company) => company.name === mostSimilarCompany) + // Create the most similar company markdown const mostSimilarCompanySection = await createSimilarCompany(companyObject) // Get the most and least similar interactions from the similarCompanies object using companyObject name const mostSimilarInteraction = interactions.find((interaction) => interaction.name === similarCompanies[mostSimilarCompany].most_similar.name) diff --git a/cli/actions/actions/basic-reporting/github.js b/cli/actions/actions/basic-reporting/github.js index 6c483e1..164f363 100644 --- a/cli/actions/actions/basic-reporting/github.js +++ b/cli/actions/actions/basic-reporting/github.js @@ -78,14 +78,15 @@ async function readBranches () { // Loop through the branches to obtain the latest commit comment, author, and date added to the branch, and then add them to the branches array for (const branch of branches) { - const { data: commit } = await octokit.rest.repos.getBranch({ + const { data: branchCommit } = await octokit.rest.repos.getBranch({ owner: github.context.repo.owner, repo: github.context.repo.repo, branch: branch.name }) - branch.commit = commit.commit.sha - branch.author = commit.commit.author.name - branch.date = commit.commit.author.date + branch.commit = branchCommit.commit.sha + branch.author = branchCommit.commit.commit.author.name + branch.date = branchCommit.commit.commit.author.date + branch.last_commit = branchCommit.commit.commit.message } return branches @@ -141,6 +142,7 @@ async function saveReports (reports, inputs) { // Create or update each file in a single commit for (const report of reports) { + // Get the sha of the file if it exists try { const { data: file } = await octokit.rest.repos.getContent({ diff --git a/cli/actions/actions/basic-reporting/index.js b/cli/actions/actions/basic-reporting/index.js index b421935..ed7080f 100644 --- a/cli/actions/actions/basic-reporting/index.js +++ b/cli/actions/actions/basic-reporting/index.js @@ -2,6 +2,8 @@ const { readObjects, readBranches, readWorkflows, saveReports } = require('./github.js') const { createCompaniesReport } = require('./companies.js') const { createCompanyReport } = require('./company.js') +const { createStudiesReport } = require('./studies.js') +const { createStudyReport } = require('./study.js') const { createMainReport } = require('./main.js') @@ -11,6 +13,7 @@ async function run () { const inputs = { companies: await readObjects('/Companies/Companies.json'), interactions: await readObjects('/Interactions/Interactions.json'), + studies: await readObjects('/Studies/Studies.json'), branches: await readBranches(), workflows: await readWorkflows() } @@ -24,6 +27,8 @@ async function run () { const reports = { companies: `Companies/README.md`, company: `Companies/`, + studies: `Studies/README.md`, + study: `Studies/`, main: `README.md`, } @@ -31,8 +36,16 @@ async function run () { const companyFiles = await createCompanyReport(inputs.companies, inputs.interactions, reports) // Create the companies file const companiesFile = createCompaniesReport(inputs.companies) - // Create the main file + + + // Create the main report const mainFile = createMainReport(inputs) + + // Create the study files + const studyFiles = await createStudyReport(inputs.studies, inputs.companies, reports) + // Create the studies file + const studiesFile = await createStudiesReport(inputs.studies) + // Create the reports array const markdownReports = [ { @@ -45,7 +58,13 @@ async function run () { path: reports.main, content: mainFile }, - ...companyFiles + { + name: 'Studies', + path: reports.studies, + content: studiesFile + }, + ...companyFiles, + ...studyFiles ] // Write the reports diff --git a/cli/actions/actions/basic-reporting/main.js b/cli/actions/actions/basic-reporting/main.js index bc0ff94..f844a89 100644 --- a/cli/actions/actions/basic-reporting/main.js +++ b/cli/actions/actions/basic-reporting/main.js @@ -20,8 +20,8 @@ function createMainReport (inputs) { readme += mrMarkdownBuilder.h2(`Interactions`) readme += `Interaction objects are essentially content related to a company. They can include meeting notes, emails, product documentation, blog posts, audio transcripts, and more. While each interaction is linked to a company access to the interaction is presented handled by the company that owns it.` - readme += mrMarkdownBuilder.h2(`Studies`) - readme += `Study objects will be a part of a future release of Mediumroast and will be a part of a paid subscription. Stay tuned for more information on this feature and a way to gain access to it.` + readme += mrMarkdownBuilder.h2(`Studies [${mrMarkdownBuilder.link('View Studies', './Studies/README.md')}]`) + readme += `The first implementation of studies is available via the \`Foundation\` study. The \`Foundation\` study includes all Companies, and their associated Interactions, building atop the analysis that Caffeine performed on Interactions data. To initialize the \`Foundation\` study please run \`mrcli study --init_foundation\` and then reach out to your Mediumroast team via ${mrMarkdownBuilder.link('Discord', 'https://discord.gg/ebM4Cf8meK')} or email us at ${mrMarkdownBuilder.link('hello@mediumroast.io', 'mailto:hello@mediumroast.io')}. Note that activating studies requires a professional services engagement with your Mediumroast crew. Finally, support for user defined studies will come in a future release.\n` readme += mrMarkdownBuilder.h2('Navigation and Modification') readme +=`Direct navigation and modifcation of repository contents is not recommended. Instead this README file, and accompanying markdown files, will guide you through its contents. Additionally, the open source node module and CLI \`mediumroast_js\` [${mrMarkdownBuilder.link('GitHub', 'https://github.com/mediumroast/mediumroast_js')}, ${mrMarkdownBuilder.link('NPM', 'https://www.npmjs.com/package/mediumroast_js')}] can be used to create, update, and delete content in the repository.` @@ -37,7 +37,7 @@ function createMainReport (inputs) { const month = date.toLocaleString('default', { month: 'long' }) const year = date.getFullYear() - readme += `The repository contains \`2\` active workflows. As of \`${month}-${year}\` \`${inputs.workflows.runTime} minutes\` have been consumed. A GitHub free plan has \`2000 minutes\` available per month meaning there is \`${2000 - inputs.workflows.runTime}\` remaining minutes for the month. Assuming a repository with 10s of company objects, each workflow runs about a minute at midnight everyday. This means a good hueristic for how many minutes are consumed in a month is 2 workflows/day x 1 min/workflow x 30 days/month or \`${2*1*30} min/month\`. To get an accurate view of your consumed minutes for your planning please run \`mrcli billing\`. The statuses of five most recent workflow runs are provided below, links are included to enable more information on the workflows.\n` + readme += `The repository contains \`2\` active workflows. As of \`${month}-${year}\` \`${inputs.workflows.runTime} minutes\` have been consumed. A GitHub free plan has \`2000 minutes\` available per month meaning there is \`${2000 - inputs.workflows.runTime}\` remaining minutes for the month. Assuming a repository with 10s of company objects, each workflow runs about a minute at midnight everyday. A good estimate for how many minutes are typically consumed in a month is 2 workflows/day x 1 min/workflow x 30 days/month or \`${2*1*30} min/month\`. To get an accurate view of your consumed minutes for your planning please run \`mrcli actions\`. The statuses of five most recent workflow runs are provided below, links are included to enable more information on the workflows.\n` // Create the table header const workflowTableHeader = mrMarkdownBuilder.tableHeader(['Workflow Name', 'Last Status', 'Run Time Message', 'Run Time']) // Create the table rows @@ -63,11 +63,13 @@ function createMainReport (inputs) { readme += mrMarkdownBuilder.h2('Branches') readme += `The repository contains \`${inputs.branches.length}\` branches. The last commit of each branch is listed below. Click on the branch name to view the branch.\n` // Create the table header - const branchTableHeader = mrMarkdownBuilder.tableHeader(['Branch Name', 'Last Commit']) + const branchTableHeader = mrMarkdownBuilder.tableHeader(['Branch Name', 'Commit Author', 'Commit Date', 'Last Commit Message']) // Create the table rows const branchTableRows = inputs.branches.map((branch) => { const branchRow = [ mrMarkdownBuilder.link(branch.name, `./tree/${branch.name}`), + branch.author, + branch.date, branch.last_commit ] return branchRow diff --git a/cli/actions/actions/basic-reporting/package.json b/cli/actions/actions/basic-reporting/package.json index 8f6a971..6548c2b 100644 --- a/cli/actions/actions/basic-reporting/package.json +++ b/cli/actions/actions/basic-reporting/package.json @@ -1,6 +1,6 @@ { "name": "basic-reporting", - "version": "1.0.0", + "version": "1.5.0", "description": "", "main": "index.js", "scripts": { diff --git a/cli/actions/actions/basic-reporting/studies.js b/cli/actions/actions/basic-reporting/studies.js new file mode 100644 index 0000000..1d98866 --- /dev/null +++ b/cli/actions/actions/basic-reporting/studies.js @@ -0,0 +1,45 @@ +const mrMarkdownBuilder = require('mr_markdown_builder') + +// Globals +const NO_STUDIES = `${mrMarkdownBuilder.h1('Notice')}There are no studies present in your Mediumroast for GitHub repository. Please initialize the \`Foundation\` study using \`mrcli study --init_foundation\` command. After you've initialized the \`Foundation\` study please reach out to your Mediumroast team via ${mrMarkdownBuilder.link('Discord', 'https://discord.gg/ebM4Cf8meK')} or email us at ${mrMarkdownBuilder.link('hello@mediumroast.io', 'mailto:hello@mediumroast.io')}. ${mrMarkdownBuilder.cr()}${mrMarkdownBuilder.cr()}` + +function createStudiesReport (studies) { + let readme = `[${mrMarkdownBuilder.link('Back to main README', '../README.md')}]\n` + readme += mrMarkdownBuilder.hr() + readme += mrMarkdownBuilder.h1('Introduction') + readme += `There are currently \`${studies.length}\` study or studies in the repository. The table below lists all available studies and some of their characteristics. Click on the study name to view the study's profile.` + + // Should we not have any studies then we should return the NO_STUDIES message + if (studies.length === 0) { + readme += NO_STUDIES + return readme + } + + readme += mrMarkdownBuilder.h1('Table of Studies') + // Create the table header + const tableHeader = mrMarkdownBuilder.tableHeader(['Study Name', 'Related GitHub Project', 'Total Associated Companies', 'Caffinated']) + // Create the table rows + const tableRows = studies.map((study) => { + // Get the most recent key from the companies object + const mostRecentKey = Object.keys(study.companies).sort().pop(); + const includedCompaniesLength = study.companies[mostRecentKey].included_companies.length; + + const studyRow = [ + mrMarkdownBuilder.link(study.name, `./${encodeURI(study.name.replace(/[\s,.\?!]/g, ''))}.md`), + study.project, + includedCompaniesLength, + study.status === 1 ? "Yes" : "No" + ] + return studyRow + }) + // Create the table + const studyTable = tableHeader + "\n" + mrMarkdownBuilder.tableRows(tableRows) + // Create the README.md file + readme += studyTable + // Return the file content + return readme +} + +module.exports = { + createStudiesReport +} \ No newline at end of file diff --git a/cli/actions/actions/basic-reporting/study.js b/cli/actions/actions/basic-reporting/study.js new file mode 100644 index 0000000..4f5d207 --- /dev/null +++ b/cli/actions/actions/basic-reporting/study.js @@ -0,0 +1,205 @@ +const mrMarkdownBuilder = require('mr_markdown_builder') +const { createTableOfCompanies } = require('./companies.js') + +// Globals +const UNCAFFINATED = `${mrMarkdownBuilder.h1('Notice')}Your study hasn't been analyzed by Mediumroast's Caffeine Machine Intelligence service yet. Please reach out to your Mediumroast team via ${mrMarkdownBuilder.link('Discord', 'https://discord.gg/ebM4Cf8meK')} or email us at ${mrMarkdownBuilder.link('help@mediumroast.io', 'mailto:help@mediumroast.io')} to arrange for onboarding, preparation and caffinating your study.` + +const NO_TOP_INSIGHTS = `There are no insights for this study. This means for the Interactions collected for this study, there are no insights that are considered to be of high importance. If you would like to learn more about how to improve the quality of the insights for this study, please reach out to your Mediumroast team via ${mrMarkdownBuilder.link('Discord', 'https://discord.gg/ebM4Cf8meK')} or email us at ${mrMarkdownBuilder.link('hello@mediumroast.io', 'mailto:hello@mediumroast.io')}.` + +const NO_COMPANY_INSIGHTS = `There are no top insights for this company due to either an insufficient number of collected Interactions or more generically none of the thresholds triggering an important insight were met. If you would like to learn more about improving insights quality and output for this company, please reach out to your Mediumroast team via ${mrMarkdownBuilder.link('Discord', 'https://discord.gg/ebM4Cf8meK')} or email us at ${mrMarkdownBuilder.link('hello@mediumroast.io', 'mailto:hello@mediumroast.io')}.` + +function _checkCaffinated (study) { + if (study.status === 0) { + return [false, UNCAFFINATED] + } + return [true, null] +} + +function _getLargestKey (sourceData) { + return Object.keys(sourceData).reduce((a, b) => sourceData[a].length > sourceData[b].length ? a : b); +} + +function _filterTopInsights (companyData, topCount=5) { + // Define the topCount interactions report data + const reportData = {}; + + // Count occurrences in targets + for (const company in companyData) { + if (companyData.hasOwnProperty(company)) { + const items = companyData[company]; + const interactionCountInTargets = {}; + + for (const key in items) { + if (items.hasOwnProperty(key)) { + const item = items[key]; + const interaction = item.source_interaction; + + for (const target in item.targets) { + if (item.targets.hasOwnProperty(target)) { + if (!interactionCountInTargets[interaction]) { + interactionCountInTargets[interaction] = 0; + } + interactionCountInTargets[interaction]++; + } + } + } + } + + // Get topCount interactions + const topInteractions = Object.entries(interactionCountInTargets) + .sort((a, b) => b[1] - a[1]) + .slice(0, topCount) + .map(entry => entry[0]); + + // Gather insights for topCount interactions + reportData[company] = {}; + for (const key in items) { + if (items.hasOwnProperty(key)) { + const item = items[key]; + const interaction = item.source_interaction; + + if (topInteractions.includes(interaction)) { + const avgSimilarityScore = Object.values(item.targets).reduce((a, b) => a + b, 0) / Object.values(item.targets).length; + if (!reportData[company][interaction]) { + reportData[company][interaction] = []; + } + reportData[company][interaction].push({ + insight: item.insight, + type: item.type, + count: item.count, + avgSimilarityScore: avgSimilarityScore, + excerpts: item.excerpts + }); + } + } + } + + // Sort report data by Count in descending order + for (const interaction in reportData[company]) { + if (reportData[company].hasOwnProperty(interaction)) { + reportData[company][interaction].sort((a, b) => b.count - a.count); + } + } + } + } + + return reportData; +} + +async function _getTopInsights(study, topCount=5) { + // Define the top five insights documents + let topInsightsDoc = "" + + // Check if sourceTopics and companies are not empty + if (!study.sourceTopics || Object.keys(study.sourceTopics).length === 0) { + return NO_TOP_INSIGHTS; + } + if (!study.companies || Object.keys(study.companies).length === 0) { + return NO_TOP_INSIGHTS; + } + + // Get the largest key from sourceData + const insightsLargestKey = _getLargestKey(study.sourceTopics); + const companiesInsightsData = study.sourceTopics[insightsLargestKey]; + + // Get the largest key from companies + const companiesLargestKey = _getLargestKey(study.companies); + const totalCompanies = study.companies[companiesLargestKey].included_companies.length; + + // largestKey is an ISO date time stamp that looks like this: 1733856213.4864123. We need to convert it to a human readable date time stamp + const date = new Date(insightsLargestKey * 1000); + + // Create the introduction to the top 5 insights + topInsightsDoc += mrMarkdownBuilder.h2(`Top Source Insights as of \`${date.toDateString()}\``) + topInsightsDoc += `The following insights are automatically generated by Mediumroast's Caffeine Machine Intelligence service, and derived from Interactions associated to the Companies in the study. This analysis means to help you understand the most important insights for the \`${totalCompanies}\` Companies in the study. For brevity only the first two insights are reported. A more complete equivalent report, in Microsoft Excel, can also be generated by running the command \`\`\`mrcli study --report=${study.name}\`\`\`.\n` + + // Filter the top insights + const topInsights = _filterTopInsights(companiesInsightsData, topCount); + + // Iterate over the top insights and create a list of insights we should start with the parent company as a third level header, then the source interaction as a fourth level header, then the insight as a list item below the source interaction + + for (const company of Object.keys(topInsights)) { + topInsightsDoc += mrMarkdownBuilder.h3(company) + for (const interaction of Object.keys(topInsights[company])) { + topInsightsDoc += mrMarkdownBuilder.h4(interaction) + for (const insight of topInsights[company][interaction].slice(0, 2)) { + topInsightsDoc += `${mrMarkdownBuilder.b('Insight:')} ${insight.insight} ${mrMarkdownBuilder.cr()}` + const insightDetails = [ + `${mrMarkdownBuilder.b('Type:')} ${insight.type}`, + `${mrMarkdownBuilder.b('Average Similarity Score:')} ${insight.avgSimilarityScore.toFixed(3)}`, + `${mrMarkdownBuilder.b('Excerpts:')} ${insight.excerpts}` + ] + topInsightsDoc += mrMarkdownBuilder.collapsible ( + 'Insight Details, click to expand', + mrMarkdownBuilder.ul(insightDetails) + ) + topInsightsDoc += mrMarkdownBuilder.cr() + } + } + topInsightsDoc += mrMarkdownBuilder.hr() + } + + return topInsightsDoc +} + +async function createStudyReport (studies, companies, reports) { + let studyReports = [] + const suffix = '.md' + const prefix = reports.study + for (let study of studies) { + // ---- BEGIN Study Introduction ---- + // Get the name of the study and remove spaces, commas, periods, question marks, and exclamation points + const studyFileName = study.name.replace(/[\s,.\?!]/g, '') + // Add company name with the logo image + // Call the h1 method from the headers module + let studyFile = `[${mrMarkdownBuilder.link('Back to Study Directory', './README.md')}]\n` + studyFile += mrMarkdownBuilder.hr() + studyFile += mrMarkdownBuilder.h1(`${study.name} Study`) + // Add a line break + studyFile += mrMarkdownBuilder.cr() + // Add the study description + studyFile += `${mrMarkdownBuilder.b('Description:')} ${study.description} ${mrMarkdownBuilder.cr()}` + // Add a horizontal rule + studyFile += mrMarkdownBuilder.hr() + // Add a line break + studyFile += mrMarkdownBuilder.cr() + // ---- END Study Introduction ---- + + // ---- Check if the study is caffinated ---- + const [caffinated, caffinatedMessage] = _checkCaffinated(study) + console.log('CAFFINATED STATUS>>>',caffinated) + if (caffinated === true) { + // ---- BEGIN Top Insights ---- + // Get the top insights + const topInsights = await _getTopInsights(study) + console.log('TOP INSIGHTS>>>',topInsights) + studyFile += topInsights + // ---- END Top Insights ---- + } else { + studyFile += caffinatedMessage + } + + // ---- BEGIN Footer ---- + // Add a line break + studyFile += mrMarkdownBuilder.cr() + + // Add a horizontal rule <-- Not needed in the footer + // studyFile += mrMarkdownBuilder.hr() + + // Add the creation date + studyFile += `[ ${mrMarkdownBuilder.b('Created:')} ${study.creation_date} by ${study.creator_name} | ${mrMarkdownBuilder.b('Modified:')} ${study.modification_date} ]` + + // Return the file content + studyReports.push({ + name: study.name, + path: `${prefix}${studyFileName}${suffix}`, + content: studyFile + }) + // ---- END Footer ---- + } + return studyReports +} + +module.exports = { + createStudyReport +} \ No newline at end of file diff --git a/cli/mrcli-billing.js b/cli/mrcli-billing.js deleted file mode 100644 index 41d2db3..0000000 --- a/cli/mrcli-billing.js +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env node - -/** - * A CLI utility used for accessing and reporting on mediumroast.io user objects - * @author Michael Hay - * @file billing.js - * @copyright 2024 Mediumroast, Inc. All rights reserved. - * @license Apache-2.0 - * @verstion 2.0.0 - */ -import chalk from 'chalk' -console.log(chalk.bold.yellow('WARNING: The billing subcommand is deprecated in this release, and replaced by actions and storage subcommands,\n\ttry \'mrcli actions --help\' or \'mrcli storage --help\' for more information.')) -process.exit() - - - -// Related object type -const objectType = 'Billings' - -// Environmentals object -const environment = new Environmentals( - '2.0', - `${objectType}`, - `Command line interface to report on consumed units of GitHub actions and storage.`, - objectType -) - -/* - ----------------------------------------------------------------------- - - FUNCTIONS - Key functions needed for MAIN - - ----------------------------------------------------------------------- -*/ - - -/* - ----------------------------------------------------------------------- - - MAIN - Steps below represent the main function of the program - - ----------------------------------------------------------------------- -*/ - -// Create the environmental settings -let myProgram = environment.parseCLIArgs(true) -myProgram - .option('-s, --storage', 'Return all storage billing information for the GitHub organization') - .option('-a, --actions', 'Return all actions billing information for the GitHub organization') - -// Remove command line options for reset_by_type, delete, update, and add_wizard by calling the removeArgByName method in the environmentals class -myProgram = environment.removeArgByName(myProgram, '--delete') -myProgram = environment.removeArgByName(myProgram, '--update') -myProgram = environment.removeArgByName(myProgram, '--add_wizard') -myProgram = environment.removeArgByName(myProgram, '--reset_by_type') -myProgram = environment.removeArgByName(myProgram, '--report') -myProgram = environment.removeArgByName(myProgram, '--find_by_name') -myProgram = environment.removeArgByName(myProgram, '--find_by_x') -myProgram = environment.removeArgByName(myProgram, '--find_by_id') -myProgram = environment.removeArgByName(myProgram, '--report') -myProgram = environment.removeArgByName(myProgram, '--package') -myProgram = environment.removeArgByName(myProgram, '--splash') - -// Parse the command line arguments into myArgs and obtain the options -let myArgs = myProgram.parse(process.argv) -myArgs = myArgs.opts() - -const myConfig = environment.readConfig(myArgs.conf_file) -let myEnv = environment.getEnv(myArgs, myConfig) -const accessToken = await environment.verifyAccessToken() -const processName = 'mrcli-billing' - -// Output object -const output = new CLIOutput(myEnv, objectType) - -// Construct the controller objects -const billingsCtl = new Billings(accessToken, myEnv.gitHubOrg, processName) - -// Predefine the results variable -let [success, stat, results] = [null, null, null] - -if (myArgs.actions) { - [success, stat, results] = await billingsCtl.getActionsBilling() - const myUserOutput = new CLIOutput(myEnv, 'ActionsBilling') - myUserOutput.outputCLI([results], myArgs.output) - process.exit() -} else if (myArgs.storage) { - [success, stat, results] = await billingsCtl.getStorageBilling() - const myUserOutput = new CLIOutput(myEnv, 'StorageBilling') - myUserOutput.outputCLI([results], myArgs.output) - process.exit() -} else { - [success, stat, results] = await billingsCtl.getAll() - const myUserOutput = new CLIOutput(myEnv, 'AllBilling') - myUserOutput.outputCLI(results, myArgs.output) - process.exit() -} - diff --git a/cli/mrcli-storage.js b/cli/mrcli-storage.js index 434bf3b..f1efc65 100644 --- a/cli/mrcli-storage.js +++ b/cli/mrcli-storage.js @@ -53,12 +53,10 @@ let myProgram = environment.parseCLIArgs(true) // Remove command line options for reset_by_type, delete, update, and add_wizard by calling the removeArgByName method in the environmentals class myProgram = environment.removeArgByName(myProgram, '--delete') myProgram = environment.removeArgByName(myProgram, '--add_wizard') -myProgram = environment.removeArgByName(myProgram, '--reset_by_type') +myProgram = environment.removeArgByName(myProgram, '--reset_by_name') myProgram = environment.removeArgByName(myProgram, '--report') myProgram = environment.removeArgByName(myProgram, '--find_by_name') myProgram = environment.removeArgByName(myProgram, '--find_by_x') -myProgram = environment.removeArgByName(myProgram, '--find_by_id') -myProgram = environment.removeArgByName(myProgram, '--report') myProgram = environment.removeArgByName(myProgram, '--package') myProgram = environment.removeArgByName(myProgram, '--splash') myProgram = environment.removeArgByName(myProgram, '--update') diff --git a/cli/mrcli-study.js b/cli/mrcli-study.js index e0555e0..ee1e296 100755 --- a/cli/mrcli-study.js +++ b/cli/mrcli-study.js @@ -13,61 +13,135 @@ // Import required modules -import { Studies } from '../src/api/gitHubServer.js' -// NOTE: When we have a study wizard, we will need to import it here -// import AddStudy from '../src/cli/studyWizard.js' +import { Interactions, Companies, Studies, Users } from '../src/api/gitHubServer.js' +import AddStudy from '../src/cli/studyWizard.js' +import GitHubFunctions from '../src/api/github.js' import Environmentals from '../src/cli/env.js' import CLIOutput from '../src/cli/output.js' -import chalk from 'chalk' - -console.log(chalk.bold.yellow('NOTICE: Studies aren\'t supported in this release, exiting.')) -process.exit() +import CLIUtilities from '../src/cli/common.js' +import { SourceInsights } from '../src/report/studies.js' +import ora from 'ora' +import { GitHubAuth } from '../src/api/authorize.js' // Globals const objectType = 'Studies' // Construct the CLI object const environment = new Environmentals ( - '3.0', + '3.0.0', `${objectType}`, `A CLI utility to manage and report on Mediumroast for GitHub Study objects`, objectType ) +// Read the command line switches +let myProgram = environment.parseCLIArgs(true) +myProgram = environment.removeArgByName(myProgram, '--update') +myProgram = environment.removeArgByName(myProgram, '--persona') +myProgram = environment.removeArgByName(myProgram, '--package') +myProgram = environment.removeArgByName(myProgram, '--delete') +myProgram = environment.removeArgByName(myProgram, '--add_wizard') +myProgram = environment.removeArgByName(myProgram, '--report') +myProgram.option('-i, --init_foundation ', 'Initialize Foundation Study.') +myProgram.option('--report ', 'Generate a report for the specified study.') +// Parse the command line arguments +let myArgs = myProgram.parse(process.argv) +myArgs = myArgs.opts() + // Create the environmental settings -const myArgs = environment.parseCLIArgs() const myConfig = environment.readConfig(myArgs.conf_file) const myEnv = environment.getEnv(myArgs, myConfig) -const accessToken = await environment.verifyAccessToken() +const myAuth = new GitHubAuth(myEnv, environment, myArgs.conf_file, true) +const verifiedToken = await myAuth.verifyAccessToken() +let accessToken = null +if (!verifiedToken[0]) { + console.error(`ERROR: ${verifiedToken[1].status_msg}`) + process.exit(-1) +} else { + accessToken = verifiedToken[2].token +} const processName = 'mrcli-study' // Output object const output = new CLIOutput(myEnv, objectType) +// CLI Utilities object +const cliUtils = new CLIUtilities() + // Construct the controller objects +const companyCtl = new Companies(accessToken, myEnv.gitHubOrg, processName) +const interactionCtl = new Interactions(accessToken, myEnv.gitHubOrg, processName) +const gitHubCtl = new GitHubFunctions(accessToken, myEnv.gitHubOrg, processName) const studyCtl = new Studies(accessToken, myEnv.gitHubOrg, processName) +const userCtl = new Users(accessToken, myEnv.gitHubOrg, processName) // Predefine the results variable -let [success, stat, results] = [null, null, null] +let success = Boolean() +let stat = Object() || {} +let results = Array() || [] // Process the cli options -if (myArgs.find_by_id) { - console.error('ERROR (%d): Find by id not implemented.', -1) - process.exit(-1) -} else if (myArgs.find_by_name) { +if (myArgs.find_by_name) { [success, stat, results] = await studyCtl.findByName(myArgs.find_by_name) } else if (myArgs.find_by_x) { const myCLIObj = JSON.parse(myArgs.find_by_x) const toFind = Object.entries(myCLIObj)[0] [success, stat, results] = await studyCtl.findByX(toFind[0], toFind[1]) -} else if (myArgs.create) { - console.error('ERROR (%d): Create not yet implemented.', -1) - process.exit(-1) - //results = await studyCtl.delete(myArgs.delete) } else if (myArgs.delete) { console.error('ERROR (%d): Delete not implemented.', -1) process.exit(-1) //results = await studyCtl.delete(myArgs.delete) +} else if (myArgs.init_foundation) { + // Check for a lock in the repository + const lockResp = await studyCtl.checkForLock() + if (lockResp[0]) { + console.log(`ERROR: ${lockResp[1].status_msg}`) + process.exit(-1) + } + // Get all objects + const allObjects = await cliUtils.getAllObjects({ + companies: companyCtl, + interactions: interactionCtl, + studies: studyCtl, + users: userCtl, + }) + const newStudy = new AddStudy(myEnv, { github: gitHubCtl, interaction: interactionCtl, company: companyCtl, user: userCtl, study: studyCtl }, allObjects[2]) + const result = await newStudy.wizard() + if (result[0]) { + console.log(`SUCCESS: ${result[1].status_msg}`) + process.exit(0) + } else { + console.log(`ERROR: ${result[1].status_msg}`) + process.exit(-1) + } +} else if (myArgs.reset_by_name) { + const resetResp = await cliUtils.resetStatus(myArgs.reset_by_name, studyCtl) + if (resetResp[0]) { + console.log(`SUCCESS: ${resetResp[1].status_msg}`) + process.exit(0) + } else { + console.log(`ERROR: ${resetResp[1].status_msg}`) + process.exit(-1) + } +} else if (myArgs.report) { + const studyName = myArgs.report + const studyResults = await studyCtl.findByName(studyName) + const study = studyResults[2][0] + + const docController = new SourceInsights( + studyName, + study, + myEnv + ) + + const writeResult = await docController.generateTop5InsightsReport() + if (!writeResult[0]) { + console.error(`ERROR: ${writeResult[1].status_msg}`) + process.exit(-1) + } + console.log(`SUCCESS: ${writeResult[1].status_msg}`) + process.exit(0) + } else { [success, stat, results] = await studyCtl.getAll() results = results.mrJson diff --git a/cli/mrcli.js b/cli/mrcli.js index eb51791..943fcbf 100755 --- a/cli/mrcli.js +++ b/cli/mrcli.js @@ -1,4 +1,4 @@ -#!/usr/bin/env node --no-deprecation +#!/usr/bin/env -S node --no-deprecation /** * @fileoverview The wrapping CLI for Mediumroast for GitHub @@ -13,23 +13,11 @@ // Import required modules import program from 'commander' -import fs from 'fs' -import path from 'path' -import { fileURLToPath } from 'url'; +import CLIUtilities from '../src/cli/common.js' -// Function to update version from package.json -function updateVersionFromPackageJson() { - const __filename = fileURLToPath(import.meta.url); - const __dirname = path.dirname(__filename); - const packageJsonPath = path.join(__dirname, '..', 'package.json'); - const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); - const versionNumber = packageJson.version; - - program.version(versionNumber); -} - -// Update the version from package.json -updateVersionFromPackageJson() +// Construct the CLIUtilities object & set the version +const cliUtils = new CLIUtilities() +program.version(cliUtils.getVersionFromPackageJson()) program .name('mrcli') diff --git a/cli/tutorials.json b/cli/tutorials.json index 45e9c18..9533d5f 100644 --- a/cli/tutorials.json +++ b/cli/tutorials.json @@ -7,5 +7,8 @@ }, "Company": { "title": "Company Subcommand" + }, + "Study": { + "title": "Study Subcommand" } } \ No newline at end of file diff --git a/docs/GitHubFunctions.GitHubFunctions.html b/docs/GitHubFunctions.GitHubFunctions.html index b962239..ab552ac 100644 --- a/docs/GitHubFunctions.GitHubFunctions.html +++ b/docs/GitHubFunctions.GitHubFunctions.html @@ -34,21 +34,21 @@ @@ -345,9 +345,9 @@ - Documentation generated by JSDoc 3.6.11 + Documentation generated by JSDoc 4.0.4 - on Tue Sep 17th 2024 + on Sat Dec 28th 2024 using the DocStrap template. diff --git a/docs/authorize.js.html b/docs/authorize.js.html index 41d7ff1..183825a 100644 --- a/docs/authorize.js.html +++ b/docs/authorize.js.html @@ -34,21 +34,21 @@ @@ -101,7 +101,7 @@

Source: authorize.js

* @requires open * @requires octoDevAuth * @requires chalk - * @requires cli-table + * @requires cli-table3 * @requires configparser * @requires FilesystemOperators * @@ -117,7 +117,7 @@

Source: authorize.js

import open from "open" import * as octoDevAuth from '@octokit/auth-oauth-device' import chalk from "chalk" -import Table from 'cli-table' +import Table from 'cli-table3' import FilesystemOperators from '../cli/filesystem.js' @@ -199,15 +199,30 @@

Source: authorize.js

deviceCode = verifier.device_code // Print the verification artifact to the console console.log( - chalk.blue.bold(`If your OS supports it, opening your browser, otherwise, navigate to the Authorization website. Then, please copy and paste the Authorization code into your browser.\n`) + chalk.blue.bold(`If supported opening your browser to the Authorization website.\nIf your browser doesn't open, please copy and paste the Authorization website URL into your browser's address bar.\n`) ) + const authWebsitePrefix = `Authorization website:` + const authCodePrefix = `Authorization code:` + const authWebsite = chalk.bold.red(verifier.verification_uri) + const authCode = chalk.bold.red(verifier.user_code) const table = new Table({ rows: [ - [chalk.blue.bold(`Authorization website:`), chalk.bold.red(verifier.verification_uri)], - [chalk.blue.bold(`Authorization code:`), chalk.bold.red(verifier.user_code)] + [authWebsitePrefix, authWebsite], + [authCodePrefix, authCode] ] }) - console.log(table.toString()) + // Get the table as a string + const tableString = table.toString() + // Check to see if the table string is empty + if (tableString !== '') { + // Print the table to the console, since not empty + console.log(tableString) + } else { + // Print strings to the console, since empty + console.log(`\t${authWebsitePrefix} ${authWebsite}`) + console.log(`\t${authCodePrefix} ${authCode}`) + } + console.log(`\nCopy and paste the Authorization code into correct field on the Authorization website. Once authorized setup will continue.\n`) open(verifier.verification_uri) } }) @@ -355,9 +370,9 @@ - Documentation generated by JSDoc 3.6.11 + Documentation generated by JSDoc 4.0.4 - on Tue Sep 17th 2024 + on Sat Dec 28th 2024 using the DocStrap template. diff --git a/docs/classes.list.html b/docs/classes.list.html index 6149289..cf9a57c 100644 --- a/docs/classes.list.html +++ b/docs/classes.list.html @@ -34,21 +34,21 @@ @@ -147,34 +147,6 @@

-

Classes

- -
-
GitHubFunctions
-
- -
Actions
-
- -
Companies
-
- -
Interactions
-
- -
Storage
-
- -
Studies
-
- -
Users
-
- -
GitHubAuth
-
-
- @@ -233,9 +205,9 @@ - Documentation generated by JSDoc 3.6.11 + Documentation generated by JSDoc 4.0.4 - on Tue Sep 17th 2024 + on Sat Dec 28th 2024 using the DocStrap template. diff --git a/docs/gitHubServer.js.html b/docs/gitHubServer.js.html index f7c5768..e1f942f 100644 --- a/docs/gitHubServer.js.html +++ b/docs/gitHubServer.js.html @@ -34,21 +34,21 @@ @@ -705,9 +705,9 @@ - Documentation generated by JSDoc 3.6.11 + Documentation generated by JSDoc 4.0.4 - on Tue Sep 17th 2024 + on Sat Dec 28th 2024 using the DocStrap template. diff --git a/docs/github.js.html b/docs/github.js.html index 5a43c64..dfd9f68 100644 --- a/docs/github.js.html +++ b/docs/github.js.html @@ -34,21 +34,21 @@ @@ -1280,9 +1280,9 @@ - Documentation generated by JSDoc 3.6.11 + Documentation generated by JSDoc 4.0.4 - on Tue Sep 17th 2024 + on Sat Dec 28th 2024 using the DocStrap template. diff --git a/docs/index.html b/docs/index.html index e6f8547..55707e2 100644 --- a/docs/index.html +++ b/docs/index.html @@ -34,21 +34,21 @@ @@ -102,14 +102,10 @@

Welcome Mediumroast for GitHub

-

Products organizations must build robust product plans from competitive and customer interactions everyone can see, use, and reference. Therefore, Mediumroast for GitHub intends to help Products oranizations construct an active interactions repository close to the action of development and issue management in GitHub.

-

Notices

-
    -
  • A new version of the CLI is available and the major focus of this version is to add in Competitive Similarity Analysis, Interaction summarization and Interaction Proto-requirements discovery.
  • -
  • You can review the GitHub Page Version rather than the repository version of this documentation, but the screencasts of several of the CLI tutorials will not display.
  • -
+

Products organizations must build evidence based product plans from competitive and customer interactions. Using Artificial Intelligence and Machine Learning, Mediumroast for GitHub realizes an active product planning repository for Product Managers and Developers everyone can see, use, and reference. The repository is built on GitHub and is accessible via Command Line Interface (CLI) and a Software Development Kit (SDK). This CLI and SDK are built on Node.js and is available for Linux and MacOS operating systems.

+

A GitHub Page Version of this content is also available, but the screencasts and videos won't display.

Installation and configuration

-

Mediumroast for GitHub includes a GitHub Application, a Command Line Interface, and a Software Development Kit. The following steps show you how to install the App and the CLI with SDK.

+

Mediumroast for GitHub includes a GitHub Application, a Command Line Interface, and a Software Development Kit. The following steps show you how to install the App and the CLI with SDK.

Preinstallation requirements

  1. A GitHub organization
  2. @@ -119,7 +115,7 @@

    Preinstallation requirements

  3. NPM installed, ideally globally for all users.

Step 1 - Install the GitHub App

-

Browse to the Mediumroast for GitHub GitHub Application and:

+

Browse to the Mediumroast for GitHub GitHub Application and:

  1. Click install,
  2. Choose the location for the installation, usually your organization,
  3. @@ -151,7 +147,7 @@

    Step 4.2 - Setup the CLI after initial setup

    Running mrcli setup a second time on an existing repository will not create a new one, instead it will detect the existing repository and prompt to update your authentication method and report theme. This is used typically when another user in your organization needs to access the repository.

    Step 5.1 - Verify your installation via the CLI

    After the setup process is complete you can verify the installation by running mrcli company to see the companies in the repository.

    -

    Exzmple screenshot of companies table

    +

    Example screenshot of companies table

    companies_table

    Step 5.2 - Verify your installation via GitHub actions

    Additionally, two GitHub Actions are installed in the repository, one to clean up branches and the other to generate reports. To verify the actions are installed browse to the repository and click on the Actions tab to see the actions running. These actions are set to run on a schedule, but can be run manually. When a run you'll find an automatically generated README.md file in the repository and links to company reports in the README.md, an example screenshot of the repository companies report is below.

    @@ -162,7 +158,17 @@

    Warning

    Contributing

    If you're interested in contributing to the Mediumroast for GitHub project, please review the CONTRIBUTING.md file in the .github directory of the repository. The file contains information on how to get started, how to clone the repository, and how to install the SDK for development.

    Issues

    -

    If you detect a problem or want to suggest an improvement open an issue and we will work with you to resolve or respond.

+

If you detect a problem or want to suggest an improvement open an issue and we will work with you to resolve or respond.

+

Support

+

If you need support or would like to have Mediumroast run the Caffeine Machine Intelligence service on your repository please contact the Mediumroast team via Discord or email us at [hello@mediumroast.io], (mailto:hello@mediumroast.io).

+

Release notes

+

Version 0.8.06.01

+
    +
  • The latest version of the SDK and CLI package now includes an initial implementation of Studies with the Foundation study.
  • +
  • The SDK and CLI may run on for Windows OS, but it has not been tested. If you choose to install and run into problems please open an issue and we will work with you to resolve the matter.
  • +
  • cli-table has been replaced by cli-table3 to ensure that CLI tabular outputs are on a stable and maintained package.
  • +
  • The xlsx package has been removed and replaced with exceljs for improved output to Excel files, and due to the security vulnerability in the xlsx package.
  • +
@@ -210,9 +216,9 @@ - Documentation generated by JSDoc 3.6.11 + Documentation generated by JSDoc 4.0.4 - on Tue Sep 17th 2024 + on Sat Dec 28th 2024 using the DocStrap template. diff --git a/docs/module-GitHubAuth-GitHubAuth.html b/docs/module-GitHubAuth-GitHubAuth.html index 73fc4a0..4676bd3 100644 --- a/docs/module-GitHubAuth-GitHubAuth.html +++ b/docs/module-GitHubAuth-GitHubAuth.html @@ -34,21 +34,21 @@ @@ -345,9 +345,9 @@ - Documentation generated by JSDoc 3.6.11 + Documentation generated by JSDoc 4.0.4 - on Tue Sep 17th 2024 + on Sat Dec 28th 2024 using the DocStrap template. diff --git a/docs/module-GitHubAuth.html b/docs/module-GitHubAuth.html index 055ba2c..3dfa7fd 100644 --- a/docs/module-GitHubAuth.html +++ b/docs/module-GitHubAuth.html @@ -34,21 +34,21 @@ @@ -199,7 +199,7 @@

Requires

  • module:chalk
  • -
  • module:cli-table
  • +
  • module:cli-table3
  • module:configparser
  • @@ -446,7 +446,7 @@
    Parameters:
    @@ -527,9 +527,9 @@ - Documentation generated by JSDoc 3.6.11 + Documentation generated by JSDoc 4.0.4 - on Tue Sep 17th 2024 + on Sat Dec 28th 2024 using the DocStrap template. diff --git a/docs/module-GitHubFunctions.html b/docs/module-GitHubFunctions.html index ece8d07..c1f0ff2 100644 --- a/docs/module-GitHubFunctions.html +++ b/docs/module-GitHubFunctions.html @@ -34,21 +34,21 @@ @@ -1171,9 +1171,9 @@ - Documentation generated by JSDoc 3.6.11 + Documentation generated by JSDoc 4.0.4 - on Tue Sep 17th 2024 + on Sat Dec 28th 2024 using the DocStrap template. diff --git a/docs/module-baseObjects-Actions.html b/docs/module-baseObjects-Actions.html index 0e98357..af10161 100644 --- a/docs/module-baseObjects-Actions.html +++ b/docs/module-baseObjects-Actions.html @@ -34,21 +34,21 @@ @@ -347,9 +347,9 @@ - Documentation generated by JSDoc 3.6.11 + Documentation generated by JSDoc 4.0.4 - on Tue Sep 17th 2024 + on Sat Dec 28th 2024 using the DocStrap template. diff --git a/docs/module-baseObjects-Companies.html b/docs/module-baseObjects-Companies.html index f0ea3ed..ef0b5d5 100644 --- a/docs/module-baseObjects-Companies.html +++ b/docs/module-baseObjects-Companies.html @@ -34,21 +34,21 @@ @@ -347,9 +347,9 @@ - Documentation generated by JSDoc 3.6.11 + Documentation generated by JSDoc 4.0.4 - on Tue Sep 17th 2024 + on Sat Dec 28th 2024 using the DocStrap template. diff --git a/docs/module-baseObjects-Interactions.html b/docs/module-baseObjects-Interactions.html index 93d439a..e930115 100644 --- a/docs/module-baseObjects-Interactions.html +++ b/docs/module-baseObjects-Interactions.html @@ -34,21 +34,21 @@ @@ -347,9 +347,9 @@ - Documentation generated by JSDoc 3.6.11 + Documentation generated by JSDoc 4.0.4 - on Tue Sep 17th 2024 + on Sat Dec 28th 2024 using the DocStrap template. diff --git a/docs/module-baseObjects-Storage.html b/docs/module-baseObjects-Storage.html index 2a343fa..8f97b80 100644 --- a/docs/module-baseObjects-Storage.html +++ b/docs/module-baseObjects-Storage.html @@ -34,21 +34,21 @@ @@ -347,9 +347,9 @@ - Documentation generated by JSDoc 3.6.11 + Documentation generated by JSDoc 4.0.4 - on Tue Sep 17th 2024 + on Sat Dec 28th 2024 using the DocStrap template. diff --git a/docs/module-baseObjects-Studies.html b/docs/module-baseObjects-Studies.html index 6a3cadb..9b78282 100644 --- a/docs/module-baseObjects-Studies.html +++ b/docs/module-baseObjects-Studies.html @@ -34,21 +34,21 @@ @@ -347,9 +347,9 @@ - Documentation generated by JSDoc 3.6.11 + Documentation generated by JSDoc 4.0.4 - on Tue Sep 17th 2024 + on Sat Dec 28th 2024 using the DocStrap template. diff --git a/docs/module-baseObjects-Users.html b/docs/module-baseObjects-Users.html index 5002367..4f1091a 100644 --- a/docs/module-baseObjects-Users.html +++ b/docs/module-baseObjects-Users.html @@ -34,21 +34,21 @@ @@ -347,9 +347,9 @@ - Documentation generated by JSDoc 3.6.11 + Documentation generated by JSDoc 4.0.4 - on Tue Sep 17th 2024 + on Sat Dec 28th 2024 using the DocStrap template. diff --git a/docs/module-baseObjects.html b/docs/module-baseObjects.html index 7e6a590..f145ce2 100644 --- a/docs/module-baseObjects.html +++ b/docs/module-baseObjects.html @@ -34,21 +34,21 @@ @@ -1657,9 +1657,9 @@ - Documentation generated by JSDoc 3.6.11 + Documentation generated by JSDoc 4.0.4 - on Tue Sep 17th 2024 + on Sat Dec 28th 2024 using the DocStrap template. diff --git a/docs/modules.list.html b/docs/modules.list.html index b90bbbd..655a89c 100644 --- a/docs/modules.list.html +++ b/docs/modules.list.html @@ -34,21 +34,21 @@ @@ -147,34 +147,6 @@

    -

    Classes

    - -
    -
    GitHubFunctions
    -
    - -
    Actions
    -
    - -
    Companies
    -
    - -
    Interactions
    -
    - -
    Storage
    -
    - -
    Studies
    -
    - -
    Users
    -
    - -
    GitHubAuth
    -
    -
    - @@ -233,9 +205,9 @@ - Documentation generated by JSDoc 3.6.11 + Documentation generated by JSDoc 4.0.4 - on Tue Sep 17th 2024 + on Sat Dec 28th 2024 using the DocStrap template. diff --git a/docs/quicksearch.html b/docs/quicksearch.html index fdfac1d..10a336b 100644 --- a/docs/quicksearch.html +++ b/docs/quicksearch.html @@ -7,7 +7,7 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/tutorials.list.html b/docs/tutorials.list.html index 80c3c47..953660b 100644 --- a/docs/tutorials.list.html +++ b/docs/tutorials.list.html @@ -34,21 +34,21 @@ @@ -147,34 +147,6 @@

    -

    Classes

    - -
    -
    GitHubFunctions
    -
    - -
    Actions
    -
    - -
    Companies
    -
    - -
    Interactions
    -
    - -
    Storage
    -
    - -
    Studies
    -
    - -
    Users
    -
    - -
    GitHubAuth
    -
    -
    - @@ -233,9 +205,9 @@ - Documentation generated by JSDoc 3.6.11 + Documentation generated by JSDoc 4.0.4 - on Tue Sep 17th 2024 + on Sat Dec 28th 2024 using the DocStrap template. diff --git a/package-lock.json b/package-lock.json index 8e90d77..28a02e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mediumroast_js", - "version": "0.8.02.00", + "version": "0.8.06.01", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "mediumroast_js", - "version": "0.8.02.00", + "version": "0.8.06.01", "license": "Apache-2.0", "dependencies": { "@json2csv/plainjs": "^7.0.4", @@ -14,19 +14,23 @@ "asciiart-logo": "^0.2.7", "axios": "^1.4.0", "box-plot": "^1.0.0", + "chalk": "^5.0.0", "cli-progress": "^3.11.2", + "cli-table3": "^0.6.5", "commander": "^8.3.0", "configparser": "^0.3.9", "docx": "^7.4.1", + "exceljs": "^4.4.0", "inquirer": "^9.1.2", "mediumroast_js": "^0.8.0-0.0", "node-fetch": "^3.2.10", + "node-geocoder": "^4.4.1", "octokit": "^4.0.2", "open": "^9.1.0", "ora": "^6.1.2", + "taffydb": "^2.7.3", "uninstall": "^0.0.0", - "wrap-ansi": "^8.1.0", - "xlsx": "^0.18.5" + "wrap-ansi": "^8.1.0" }, "bin": { "mrcli": "cli/mrcli.js", @@ -50,7 +54,9 @@ "chalk": "^5.0.0", "cli-table3": "^0.6.5", "echarts": "^5.5.1", + "exceljs": "^4.4.0", "ink-docstrap": "^1.3.2", + "jsdocs": "^1.0.0", "node-geocoder": "^4.4.1", "rollup": "^2.66.1", "rollup-plugin-babel": "^4.4.0", @@ -1705,6 +1711,51 @@ "node": ">=0.1.90" } }, + "node_modules/@fast-csv/format": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz", + "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isboolean": "^3.0.3", + "lodash.isequal": "^4.5.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0" + } + }, + "node_modules/@fast-csv/format/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@fast-csv/parse": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz", + "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.groupby": "^4.6.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0", + "lodash.isundefined": "^3.0.1", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/@fast-csv/parse/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -2378,6 +2429,62 @@ "node": ">=4" } }, + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver/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, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -2419,6 +2526,13 @@ "node": ">=4" } }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2590,6 +2704,20 @@ "node": ">=0.6" } }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, "node_modules/bl": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.0.0.tgz", @@ -2623,25 +2751,6 @@ "ieee754": "^1.2.1" } }, - "node_modules/bl/node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/bl/node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -2743,12 +2852,66 @@ "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA==", "license": "MIT" }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "license": "BSD-3-Clause" }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "dev": true, + "engines": { + "node": ">=0.2.0" + } + }, "node_modules/builtin-modules": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", @@ -2875,6 +3038,19 @@ "node": ">=0.8" } }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dev": true, + "license": "MIT/X11", + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/chalk": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", @@ -3090,6 +3266,16 @@ "node": ">=0.1.90" } }, + "node_modules/colorx": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/colorx/-/colorx-0.0.7.tgz", + "integrity": "sha512-6vy2/IlhBAMmEKekgMLEUxKisWlX+tJ4SDBhHdQ8hv/f6KAwKzO3sIX13UX5fHl7n9PK6uD7kHmpyRMxf+TKGg==", + "dev": true, + "license": "MIT", + "bin": { + "colorx": "bin/colorx" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -3116,6 +3302,37 @@ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/compress-commons/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, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3188,6 +3405,35 @@ "node": ">=0.8" } }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/crc32-stream/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, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3209,6 +3455,13 @@ "node": ">= 12" } }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "dev": true, + "license": "MIT" + }, "node_modules/de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", @@ -3474,6 +3727,16 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -3517,6 +3780,16 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/entities": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", @@ -3613,6 +3886,52 @@ "node": ">=0.10.0" } }, + "node_modules/exceljs": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.4.0.tgz", + "integrity": "sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver": "^5.0.0", + "dayjs": "^1.8.34", + "fast-csv": "^4.3.1", + "jszip": "^3.10.1", + "readable-stream": "^3.6.0", + "saxes": "^5.0.1", + "tmp": "^0.2.0", + "unzipper": "^0.10.11", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/exceljs/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, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/exceljs/node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, "node_modules/execa": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", @@ -3673,6 +3992,20 @@ "node": ">=4" } }, + "node_modules/fast-csv": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz", + "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fast-csv/format": "4.3.5", + "@fast-csv/parse": "4.3.6" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -3811,6 +4144,13 @@ "node": ">=0.8" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3831,6 +4171,37 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/fstream/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3932,6 +4303,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -4071,19 +4449,39 @@ "node": ">=0.10.0" } }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "license": "MIT", - "engines": { - "node": ">=8" - } + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "license": "MIT", + "engines": { + "node": ">=8" + } }, "node_modules/inflight": { "version": "1.0.6", @@ -4412,6 +4810,46 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, + "node_modules/jsdocs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jsdocs/-/jsdocs-1.0.0.tgz", + "integrity": "sha512-6GmPLgjwy1aDNohUauo/ALWuGGYZ7snhLpVj5n55nJMdULnWjlwJ0Zk0hmq0P94cU7P8mEDBGiMav9GEehXPrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "colorx": "*", + "commander": "*", + "marked": "*", + "minitpl": "0.0.3", + "mkdirp": "0.5.0", + "xutil": "0.0.9" + }, + "bin": { + "docs": "bin/jsdocs", + "jsdocs": "bin/jsdocs" + } + }, + "node_modules/jsdocs/node_modules/minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdocs/node_modules/mkdirp": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "integrity": "sha512-xjjNGy+ry1lhtIKcr2PT6ok3aszhQfgrUDp4OZLHacgRgFmF6XR9XCOJVcXlVGQonIqXcK1DvqgKKQOPWYGSfw==", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "0.0.8" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -4533,6 +4971,19 @@ "node": ">=0.10.0" } }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, "node_modules/lie": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", @@ -4541,6 +4992,13 @@ "immediate": "~3.0.5" } }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "dev": true, + "license": "ISC" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4567,12 +5025,47 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, + "node_modules/lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -4591,12 +5084,26 @@ "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", "dev": true }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", "license": "MIT" }, + "node_modules/lodash.isnil": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", + "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.isnumber": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", @@ -4615,12 +5122,33 @@ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "license": "MIT" }, + "node_modules/lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true, + "license": "MIT" + }, "node_modules/log-symbols": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", @@ -4691,6 +5219,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/marked": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.4.tgz", + "integrity": "sha512-TCHvDqmb3ZJ4PWG7VEGVgtefA5/euFmsIhxtD0XsBxI39gUSKL81mIRFdt0AiNQozUahd4ke98ZdirExd/vSEw==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/mean-average": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/mean-average/-/mean-average-1.0.0.tgz", @@ -5298,6 +5839,16 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, + "node_modules/minitpl": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/minitpl/-/minitpl-0.0.3.tgz", + "integrity": "sha512-8+ChEb7JN7p8ObzAl9bzNVbA7rZC+9VNPxBIYr6bKGDKd4dnsZyWQPorLAtAb62u/jz4UUXnx5GQHg99LCib5w==", + "dev": true, + "license": "MIT", + "bin": { + "minitpl": "bin/minitpl" + } + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -5452,6 +6003,16 @@ "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/npm-run-path": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", @@ -6067,6 +6628,39 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/recast": { "version": "0.17.6", "resolved": "https://registry.npmjs.org/recast/-/recast-0.17.6.tgz", @@ -6505,6 +7099,19 @@ "postcss": "^7.0.27" } }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/scheduler": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", @@ -6676,6 +7283,56 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/taffydb": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.7.3.tgz", + "integrity": "sha512-GQ3gtYFSOAxSMN/apGtDKKkbJf+8izz5YfbGqIsUc7AMiQOapARZ76dhilRY2h39cynYxBFdafQo5HUL5vgkrg==", + "license": "BSD-2-Clause" + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/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, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/tar-stream/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, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -6807,6 +7464,16 @@ "node": ">=18" } }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "dev": true, + "license": "MIT/X11", + "engines": { + "node": "*" + } + }, "node_modules/ts-map": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/ts-map/-/ts-map-1.0.3.tgz", @@ -6971,6 +7638,32 @@ "node": ">=8" } }, + "node_modules/unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/unzipper/node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "dev": true, + "license": "MIT" + }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", @@ -7006,6 +7699,16 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-to-istanbul": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", @@ -7280,6 +7983,7 @@ "version": "0.18.5", "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", "dependencies": { "adler-32": "~1.3.0", "cfb": "~1.2.1", @@ -7317,6 +8021,23 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/xutil": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/xutil/-/xutil-0.0.9.tgz", + "integrity": "sha512-U3S6/3d3NOZGsONKi0v/+enJ/IEwMr0/AJlEBveO5ouM0rtGx+hPC8f5m3vAiLycP9Z11rSqUUEygj7NOKbnTw==", + "dev": true, + "license": "MIT", + "bin": { + "xutil": "bin/xutil" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -7371,6 +8092,58 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/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, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/zrender": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.0.tgz", @@ -8541,6 +9314,51 @@ "dev": true, "optional": true }, + "@fast-csv/format": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz", + "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==", + "dev": true, + "requires": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isboolean": "^3.0.3", + "lodash.isequal": "^4.5.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0" + }, + "dependencies": { + "@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "dev": true + } + } + }, + "@fast-csv/parse": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz", + "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==", + "dev": true, + "requires": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.groupby": "^4.6.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0", + "lodash.isundefined": "^3.0.1", + "lodash.uniq": "^4.5.0" + }, + "dependencies": { + "@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "dev": true + } + } + }, "@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -9033,6 +9851,52 @@ "color-convert": "^1.9.0" } }, + "archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dev": true, + "requires": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "dependencies": { + "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, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dev": true, + "requires": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + } + }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -9070,6 +9934,12 @@ "tslib": "^2.0.1" } }, + "async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -9204,6 +10074,16 @@ "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==" }, + "binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dev": true, + "requires": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + } + }, "bl": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.0.0.tgz", @@ -9223,11 +10103,6 @@ "ieee754": "^1.2.1" } }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -9300,11 +10175,39 @@ "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA==" }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true + }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, + "buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "dev": true + }, + "buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "dev": true + }, "builtin-modules": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", @@ -9383,6 +10286,15 @@ "crc-32": "~1.2.0" } }, + "chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dev": true, + "requires": { + "traverse": ">=0.3.0 <0.4" + } + }, "chalk": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", @@ -9536,6 +10448,12 @@ "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==" }, + "colorx": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/colorx/-/colorx-0.0.7.tgz", + "integrity": "sha512-6vy2/IlhBAMmEKekgMLEUxKisWlX+tJ4SDBhHdQ8hv/f6KAwKzO3sIX13UX5fHl7n9PK6uD7kHmpyRMxf+TKGg==", + "dev": true + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -9555,6 +10473,31 @@ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, + "compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dev": true, + "requires": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "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, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -9615,6 +10558,29 @@ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==" }, + "crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dev": true, + "requires": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "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, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -9630,6 +10596,12 @@ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==" }, + "dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "dev": true + }, "de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", @@ -9813,6 +10785,15 @@ } } }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, "eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -9855,6 +10836,15 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, "entities": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", @@ -9921,6 +10911,42 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "exceljs": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.4.0.tgz", + "integrity": "sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==", + "dev": true, + "requires": { + "archiver": "^5.0.0", + "dayjs": "^1.8.34", + "fast-csv": "^4.3.1", + "jszip": "^3.10.1", + "readable-stream": "^3.6.0", + "saxes": "^5.0.1", + "tmp": "^0.2.0", + "unzipper": "^0.10.11", + "uuid": "^8.3.0" + }, + "dependencies": { + "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, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true + } + } + }, "execa": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", @@ -9962,6 +10988,16 @@ "tmp": "^0.0.33" } }, + "fast-csv": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz", + "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==", + "dev": true, + "requires": { + "@fast-csv/format": "4.3.5", + "@fast-csv/parse": "4.3.6" + } + }, "fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -10040,6 +11076,12 @@ "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==" }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -10053,6 +11095,29 @@ "dev": true, "optional": true }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, "function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -10118,6 +11183,12 @@ "get-intrinsic": "^1.1.3" } }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -10214,6 +11285,11 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, "immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", @@ -10465,6 +11541,37 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, + "jsdocs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jsdocs/-/jsdocs-1.0.0.tgz", + "integrity": "sha512-6GmPLgjwy1aDNohUauo/ALWuGGYZ7snhLpVj5n55nJMdULnWjlwJ0Zk0hmq0P94cU7P8mEDBGiMav9GEehXPrQ==", + "dev": true, + "requires": { + "colorx": "*", + "commander": "*", + "marked": "*", + "minitpl": "0.0.3", + "mkdirp": "0.5.0", + "xutil": "0.0.9" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==", + "dev": true + }, + "mkdirp": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "integrity": "sha512-xjjNGy+ry1lhtIKcr2PT6ok3aszhQfgrUDp4OZLHacgRgFmF6XR9XCOJVcXlVGQonIqXcK1DvqgKKQOPWYGSfw==", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + } + } + }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -10556,6 +11663,15 @@ "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==", "dev": true }, + "lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "requires": { + "readable-stream": "^2.0.5" + } + }, "lie": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", @@ -10564,6 +11680,12 @@ "immediate": "~3.0.5" } }, + "listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "dev": true + }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -10584,12 +11706,42 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true + }, + "lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "dev": true + }, + "lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "dev": true + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "dev": true + }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", "dev": true }, + "lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==", + "dev": true + }, "lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -10606,11 +11758,23 @@ "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", "dev": true }, + "lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "dev": true + }, "lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" }, + "lodash.isnil": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", + "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==", + "dev": true + }, "lodash.isnumber": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", @@ -10626,11 +11790,29 @@ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" }, + "lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==", + "dev": true + }, "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, + "lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, "log-symbols": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", @@ -10683,6 +11865,12 @@ "semver": "^6.0.0" } }, + "marked": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.4.tgz", + "integrity": "sha512-TCHvDqmb3ZJ4PWG7VEGVgtefA5/euFmsIhxtD0XsBxI39gUSKL81mIRFdt0AiNQozUahd4ke98ZdirExd/vSEw==", + "dev": true + }, "mean-average": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/mean-average/-/mean-average-1.0.0.tgz", @@ -11127,6 +12315,12 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, + "minitpl": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/minitpl/-/minitpl-0.0.3.tgz", + "integrity": "sha512-8+ChEb7JN7p8ObzAl9bzNVbA7rZC+9VNPxBIYr6bKGDKd4dnsZyWQPorLAtAb62u/jz4UUXnx5GQHg99LCib5w==", + "dev": true + }, "mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -11230,6 +12424,12 @@ "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, "npm-run-path": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", @@ -11714,6 +12914,35 @@ "util-deprecate": "~1.0.1" } }, + "readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "requires": { + "minimatch": "^5.1.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "recast": { "version": "0.17.6", "resolved": "https://registry.npmjs.org/recast/-/recast-0.17.6.tgz", @@ -12060,6 +13289,15 @@ "postcss": "^7.0.27" } }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, "scheduler": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", @@ -12188,6 +13426,48 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "taffydb": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.7.3.tgz", + "integrity": "sha512-GQ3gtYFSOAxSMN/apGtDKKkbJf+8izz5YfbGqIsUc7AMiQOapARZ76dhilRY2h39cynYxBFdafQo5HUL5vgkrg==" + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "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, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -12293,6 +13573,12 @@ "punycode": "^2.3.1" } }, + "traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "dev": true + }, "ts-map": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/ts-map/-/ts-map-1.0.3.tgz", @@ -12418,6 +13704,32 @@ "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==" }, + "unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "dev": true, + "requires": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + }, + "dependencies": { + "bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "dev": true + } + } + }, "update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", @@ -12433,6 +13745,12 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, "v8-to-istanbul": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", @@ -12662,6 +13980,18 @@ } } }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "xutil": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/xutil/-/xutil-0.0.9.tgz", + "integrity": "sha512-U3S6/3d3NOZGsONKi0v/+enJ/IEwMr0/AJlEBveO5ouM0rtGx+hPC8f5m3vAiLycP9Z11rSqUUEygj7NOKbnTw==", + "dev": true + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -12701,6 +14031,48 @@ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true }, + "zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dev": true, + "requires": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dev": true, + "requires": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + } + }, + "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, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "zrender": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.0.tgz", diff --git a/package.json b/package.json index ff41cb9..595517a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mediumroast_js", - "version": "0.8.03.00", + "version": "0.8.06.01", "description": "A Command Line Interface (CLI) and Javascript SDK to interact with Mediumroast for GitHub.", "main": "cli/mrcli.js", "scripts": { @@ -45,7 +45,9 @@ "chalk": "^5.0.0", "cli-table3": "^0.6.5", "echarts": "^5.5.1", + "exceljs": "^4.4.0", "ink-docstrap": "^1.3.2", + "jsdocs": "^1.0.0", "node-geocoder": "^4.4.1", "rollup": "^2.66.1", "rollup-plugin-babel": "^4.4.0", @@ -70,18 +72,22 @@ "asciiart-logo": "^0.2.7", "axios": "^1.4.0", "box-plot": "^1.0.0", + "chalk": "^5.0.0", "cli-progress": "^3.11.2", + "cli-table3": "^0.6.5", "commander": "^8.3.0", "configparser": "^0.3.9", "docx": "^7.4.1", + "exceljs": "^4.4.0", "inquirer": "^9.1.2", "mediumroast_js": "^0.8.0-0.0", "node-fetch": "^3.2.10", + "node-geocoder": "^4.4.1", "octokit": "^4.0.2", "open": "^9.1.0", "ora": "^6.1.2", + "taffydb": "^2.7.3", "uninstall": "^0.0.0", - "wrap-ansi": "^8.1.0", - "xlsx": "^0.18.5" + "wrap-ansi": "^8.1.0" } } diff --git a/src/api/authorize.js b/src/api/authorize.js index 7667aef..ce51f39 100644 --- a/src/api/authorize.js +++ b/src/api/authorize.js @@ -14,7 +14,7 @@ * @requires open * @requires octoDevAuth * @requires chalk - * @requires cli-table + * @requires cli-table3 * @requires configparser * @requires FilesystemOperators * @@ -30,7 +30,7 @@ import open from "open" import * as octoDevAuth from '@octokit/auth-oauth-device' import chalk from "chalk" -import Table from 'cli-table' +import Table from 'cli-table3' import FilesystemOperators from '../cli/filesystem.js' @@ -112,15 +112,30 @@ class GitHubAuth { deviceCode = verifier.device_code // Print the verification artifact to the console console.log( - chalk.blue.bold(`If your OS supports it, opening your browser, otherwise, navigate to the Authorization website. Then, please copy and paste the Authorization code into your browser.\n`) + chalk.blue.bold(`If supported opening your browser to the Authorization website.\nIf your browser doesn't open, please copy and paste the Authorization website URL into your browser's address bar.\n`) ) + const authWebsitePrefix = `Authorization website:` + const authCodePrefix = `Authorization code:` + const authWebsite = chalk.bold.red(verifier.verification_uri) + const authCode = chalk.bold.red(verifier.user_code) const table = new Table({ rows: [ - [chalk.blue.bold(`Authorization website:`), chalk.bold.red(verifier.verification_uri)], - [chalk.blue.bold(`Authorization code:`), chalk.bold.red(verifier.user_code)] + [authWebsitePrefix, authWebsite], + [authCodePrefix, authCode] ] }) - console.log(table.toString()) + // Get the table as a string + const tableString = table.toString() + // Check to see if the table string is empty + if (tableString !== '') { + // Print the table to the console, since not empty + console.log(tableString) + } else { + // Print strings to the console, since empty + console.log(`\t${authWebsitePrefix} ${authWebsite}`) + console.log(`\t${authCodePrefix} ${authCode}`) + } + console.log(`\nCopy and paste the Authorization code into correct field on the Authorization website. Once authorized setup will continue.\n`) open(verifier.verification_uri) } }) diff --git a/src/formatters/company.js b/src/cli/attic/formatters/company.js similarity index 100% rename from src/formatters/company.js rename to src/cli/attic/formatters/company.js diff --git a/src/cli/common.js b/src/cli/common.js index 2a0ee22..3b7633f 100644 --- a/src/cli/common.js +++ b/src/cli/common.js @@ -4,9 +4,13 @@ * @file common.js * @copyright 2024 Mediumroast, Inc. All rights reserved. * @license Apache-2.0 - * @version 1.1.0 + * @version 1.2.0 */ +import fs from 'fs' +import path from 'path' +import { fileURLToPath } from 'url' + class CLIUtilities { /** @@ -48,6 +52,79 @@ class CLIUtilities { getObject(objName, objects) { return objects.filter(obj => obj.name === objName) } + + /** + * @function getOwningCompany + * @description Retrieve the owning company from an array of companies + * @param {Array} companiesArray - an array of companies + * @returns {Array} an array containing the owning company + * + * @example + * const owningCompany = utilities.getOwningCompany(companiesArray) + * console.log(`The owning company is ${owningCompany}`) + * + */ + getOwningCompany(companiesArray) { + return companiesArray.filter(company => company.role === "Owner") + } + + /** + * @function getVersionFromPackageJson + * @description Retrieve the version number from the package.json file + * @returns {String} the version number + * + * @example + * const version = utilities.getVersionFromPackageJson() + * console.log(`The version number is ${version}`) + */ + getVersionFromPackageJson() { + const __filename = fileURLToPath(import.meta.url) + const __dirname = path.dirname(__filename) + const packageJsonPath = path.join(__dirname, '../..', 'package.json') + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) + return packageJson.version + } + + /** + * @async + * @function resetStatus + * @description Reset the status of an object + * @param {String} objName - the name of the object to reset + * @param {Object} apiCtl - the controller for the object + * @param {Number} objStatus - the status to reset to + * @returns {Array} an array containing if the operation succeeded, the message, and the object + * + * @example + * const resetResult = await utilities.resetStatus(objName, apiCtl, objStatus) + * if(resetResult[0]) { + * console.log(`SUCCESS: ${resetResult[1].status_msg}`) + * } else { + * console.log(`ERROR: ${resetResult[1].status_msg}`) + * } + */ + async resetStatus(objName, apiCtl, objStatus=0) { + const myUpdate = {name: objName, value: objStatus, key: 'status'} + const updateResult = await apiCtl.updateObj(myUpdate) + if(updateResult[0]){ + return [ + true, + { + status_code: updateResult[1].status_code, + status_msg: updateResult[1].status_msg + }, + updateResult + ] + } else { + return [ + false, + { + status_code: updateResult[1].status_code, + status_msg: updateResult[1].status_msg + }, + null + ] + } + } } export default CLIUtilities diff --git a/src/cli/companyWizard.js b/src/cli/companyWizard.js index 42fbabb..07f9f14 100755 --- a/src/cli/companyWizard.js +++ b/src/cli/companyWizard.js @@ -1,5 +1,5 @@ /** - * A class used to build CLIs for accessing and reporting on mediumroast.io objects + * A class used to build CLIs for constructing Company objects * @author Michael Hay * @file companyCLIwizard.js * @copyright 2024 Mediumroast, Inc. All rights reserved. @@ -15,6 +15,7 @@ import WizardUtils from "./commonWizard.js" import CLIOutput from "./output.js" import axios from "axios" import NodeGeocoder from 'node-geocoder' +import CLIUtilities from './common.js' class AddCompany { @@ -42,6 +43,7 @@ class AddCompany { this.userCtl = apiControllers.user this.githubCtl = apiControllers.github this.default = this.env.DEFAULT + this.cliUtils = new CLIUtilities() this.firmographicsEndpoint = "/V3.0/global/company/merged/firmographics/" this.sicEndpoint = "/V3.0/na/sic/description/" @@ -51,9 +53,10 @@ class AddCompany { this.default.nominatim ? this.nominatim = this.default.nominatim : this.nominatim = this.env.nominatim // Splash screen elements - this.name = "mediumroast.io Company Wizard" - this.version = "version 2.0.0" - this.description = "Prompt based company object creation for the mediumroast.io." + this.name = "Mediumroast for GitHub" + this.version = `version ${this.cliUtils.getVersionFromPackageJson()}` + this.description = "Command line Company wizard" + this.processName = "mrcli-company-wizard" // Class globals this.defaultValue = "Unknown" diff --git a/src/cli/env.js b/src/cli/env.js index ab8de6f..2c42b31 100644 --- a/src/cli/env.js +++ b/src/cli/env.js @@ -72,10 +72,9 @@ class Environmentals { ) .option( '-o --output ', - 'Select output type: table, json, xls or csv. xls & csv will save to a file.', + 'Select output type: table, json, or csv, csv output saves to a file. Note, xlsx output is presently disabled it will be reenabled in the future.', 'table', 'json', - 'xls', 'csv' ) .option( diff --git a/src/cli/interactionWizard.js b/src/cli/interactionWizard.js index d31ab2a..e841d8e 100755 --- a/src/cli/interactionWizard.js +++ b/src/cli/interactionWizard.js @@ -1,7 +1,7 @@ #!/usr/bin/env node /** - * A class used to build CLIs for accessing and reporting on mediumroast.io objects + * A class used to build CLIs for constructing Interaction objects * @author Michael Hay * @file interactionCLIwizard.js * @copyright 2022 Mediumroast, Inc. All rights reserved. @@ -19,6 +19,7 @@ import * as progress from 'cli-progress' import ora from 'ora' import crypto from 'crypto' import { resolve } from 'path' +import CLIUtilities from './common.js' class AddInteraction { /** @@ -39,24 +40,30 @@ class AddInteraction { * @param {Object} cli - the already constructed CLI object */ constructor(env, controllers){ + // Set the environment this.env = env + // Construct commmon utilities + this.cliUtils = new CLIUtilities() + this.wutils = new WizardUtils(this.objectType) + this.output = new CLIOutput(this.env, this.objectType) + this.fileSystem = new FilesystemOperators() + // Splash screen elements - this.name = "mediumroast.io Interaction Wizard" - this.version = "version 2.0.0" - this.description = "Prompt based interaction object creation for the mediumroast.io." + this.name = "Mediumroast for GitHub" + this.version = `version ${this.cliUtils.getVersionFromPackageJson()}` + this.description = "Commandline Interaction wizard" this.processName = "mrcli-interaction-wizard" // Class globals this.defaultValue = "Unknown" this.objectType = "Interactions" - this.wutils = new WizardUtils(this.objectType) // Utilities from common wizard - this.output = new CLIOutput(this.env, this.objectType) - this.fileSystem = new FilesystemOperators() this.progressBar = new progress.SingleBar( {format: '\tProgress [{bar}] {percentage}% | ETA: {eta}s | {value}/{total}'}, progress.Presets.rect ) + + // Set the controllers this.companyCtl = controllers.company this.interactionCtl = controllers.interaction this.userCtl = controllers.user @@ -64,7 +71,6 @@ class AddInteraction { // NOTE: These follow the APA style guide for references. These will be used to dynamically create // the interaction_type attribute for the interaction object. - // this.interactionTypes = this.fileSystem.readJSONFile('./interactionTypes.json')[2] this.interactionTypes = this.fileSystem.importJSONFile('./interactionTypes.json')[2] } @@ -271,6 +277,8 @@ class AddInteraction { * @function wizard * @description Invoke the text based wizard process to add an interaction to the mediumroast.io application * @returns {List} - a list containing the result of the interaction with the mediumroast.io backend + * + * @todo Remove properties variable after checking to see if it is needed or not */ async wizard() { // Unless we suppress this print out the splash screen. @@ -301,10 +309,10 @@ class AddInteraction { // prototype below contains strings that are easier to read. Additionally, should // we wish to set some defaults for each one it is also feasible within this // prototype object to do so. - let properties = [ - "organization_id", // + // let properties = [ + // "organization_id", // - ] + // ] // Capture the current data and converto to an ISO string const myDate = new Date() diff --git a/src/cli/output.js b/src/cli/output.js index e90e16f..1376639 100644 --- a/src/cli/output.js +++ b/src/cli/output.js @@ -10,7 +10,7 @@ // Import required modules import Table from 'cli-table3' import {Parser} from '@json2csv/plainjs' -import * as XLSX from 'xlsx' +// import * as XLSX from 'xlsx' import logo from 'asciiart-logo' import FilesystemOperators from './filesystem.js' @@ -206,8 +206,10 @@ class CLIOutput { try { const csv = csvParser.parse(objects) this.fileSystem.saveTextOrBlobFile(myFile, csv) + console.log(`SUCCESS: wrote [${this.objectType}] objects to [${myFile}]`) return [true, {status_code: 200, status_msg: `wrote [${this.objectType}] objects to [${myFile}]`}, null] } catch (err) { + console.error(`ERROR: Unable to write [${this.objectType}] objects to [${myFile}] due to [${err}]`) return [false, {}, err] } } @@ -215,17 +217,20 @@ class CLIOutput { // NOTE: Not exterally facing doesn't require JSDoc signture // Purpose: Output an XLSX file to this.env.outputDir containing all object metadata outputXLS(objects) { - const fileName = 'Mr_' + this.objectType + '.xlsx' - const myFile = this.env.outputDir + '/' + fileName - try { - const mySheet = XLSX.utils.json_to_sheet(objects) - const myWorkbook = XLSX.utils.book_new() - XLSX.utils.book_append_sheet(myWorkbook, mySheet, this.objectType) - XLSX.writeFile(myWorkbook, myFile) - return [true, null] - } catch (err) { - return [false, err] - } + console.log('NOTICE: XLSX output is presently disabled, a future version will reenable it.') + return [false, {status_code: 501, status_msg: 'ERROR: XLSX output is presently disabled'}, null] + // NOTE: + // const fileName = 'Mr_' + this.objectType + '.xlsx' + // const myFile = this.env.outputDir + '/' + fileName + // try { + // const mySheet = XLSX.utils.json_to_sheet(objects) + // const myWorkbook = XLSX.utils.book_new() + // XLSX.utils.book_append_sheet(myWorkbook, mySheet, this.objectType) + // XLSX.writeFile(myWorkbook, myFile) + // return [true, null] + // } catch (err) { + // return [false, err] + // } } /** diff --git a/src/cli/studyWizard.js b/src/cli/studyWizard.js new file mode 100644 index 0000000..dd2d417 --- /dev/null +++ b/src/cli/studyWizard.js @@ -0,0 +1,201 @@ +/** + * A class used to build CLIs for constructing Study objects + * @author Michael Hay + * @file studyCLIwizard.js + * @copyright 2024 Mediumroast, Inc. All rights reserved. + * @license Apache-2.0 + * @version 1.0.0 + */ + + +// Import required modules +import chalk from 'chalk' +import ora from "ora" +import WizardUtils from "./commonWizard.js" +import CLIOutput from "./output.js" +import CLIUtilities from './common.js' + +class AddStudy { + constructor(env, controllers, objects) { + // Set the environment + this.env = env + + // Construct commmon utilities + this.cliUtils = new CLIUtilities() + this.wutils = new WizardUtils(this.objectType) + this.output = new CLIOutput(this.env, this.objectType) + + // Splash screen elements + this.name = "Mediumroast for GitHub" + this.version = `Version ${this.cliUtils.getVersionFromPackageJson()}` + this.description = "Command line Study wizard" + this.processName = "mrcli-study-wizard" + + // Class globals + this.defaultValue = "Unknown" + this.objectType = "Studies" + + // Set the controllers + this.companyCtl = controllers.company + this.interactionCtl = controllers.interaction + this.userCtl = controllers.user + this.studyCtl = controllers.study + this.githubCtl = controllers.github + + // Set the objects + this.companies = objects.companies + this.interactions = objects.interactions + this.studies = objects.studies + this.users = objects.users + } + + _getFoundationDesc(myCompany) { + return `This Foundation Study for ${myCompany.name} is automatically created by Mediumroast for GitHub to provide an out of the box analysis. It analyzes all companies in the ${myCompany.name} discovery repository to surface interesting competitive insights, proto-requirements, Interactions, and Companies. Once surfaced the intention of the Foundation Study is to guide the user on important things to act upon.` + } + + _setCompanies(companies) { + const allCompanies = companies.map(company => company.name); + const allCompaniesString = allCompanies.join(" "); + const allCompaniesHash = crypto.createHash('sha256').update(allCompaniesString).digest('hex'); + + const timestamp = (new Date()).toISOString(); + const timeStampKey = Date.now() / 1000; + + return { + [timeStampKey]: { + hash: allCompaniesHash, + included_companies: allCompanies + } + }; + } + + _getStudyPrototype(myUser, myCompany, myDateString, studyName="Foundation", isFoundation=true) { + return { + name: {consoleString: "", value: studyName}, // Assigned by the user + sourceTopics: {consoleString: "", value: {}}, // Empty, assigned by caffeine + processTopics: {consoleString: "", value: {}}, // Empty, assigned by caffeine + companies: {consoleString: "", value: _this._setCompanies(this.companies)}, // TODO: Need to assign the companies and create the hash + status: {consoleString: "", value: 0}, // Set to zero, changed by caffeine + project: {consoleString: "", value: this.defaultValue}, // Default value, assigned by caffeine + syncStatus: {consoleString: "", value: this.defaultValue}, // Default value, changed by future project sync service + organization: {consoleString: "", value: this.env.gitHubOrg}, // Set the organization to the GitHub organization + description: { + consoleString: "", + value: isFoundation ? this._getFoundationDesc(myCompany[0]) : this.defaultValue + }, // Assign the foundation description unless isFoundation is false + creator: {consoleString: "", value: myUser.login}, // Set the creator to the GitHub user + creator_id: {consoleString: "", value: myUser.id}, // Set the creator to the GitHub user + creator_name: {consoleString: "", value: myUser.name}, // Set the creator to the GitHub user + public: {consoleString: "", value: true}, // Set to true + groups: {consoleString: "", value: `${this.env.gitHubOrg}:${myUser.login}`}, // Set to the organization and user, reserved for future use + creation_date: {consoleString: "", value: myDateString}, // Set to the current date + modification_date: {consoleString: "", value: myDateString}, // Set to the current date + } + } + + async _createStudyObject(studyPrototype) { + // Loop through each study object + let myStudies = [] + let myStudy = {} + for(const attribute in studyPrototype) { + myStudy[attribute] = studyPrototype[attribute].value + } + + // Add the study to the list of studies + myStudies.push(myStudy) + this.output.printLine() + return myStudies + } + + async wizard() { + // Unless we suppress this print out the splash screen. + if (this.env.splash) { + this.output.splashScreen( + this.name, + this.version, + this.description + ) + } + + // Choose if we want to run the setup or not, and it not exit the program + const processString = "It appears you'd like to initialize the Foundation Study, right?" + const exitString = "\tOk, exiting study management." + const doSetup = await this.wutils.operationOrNot(processString) + if (!doSetup) { + console.log(chalk.red.bold(exitString)) + process.exit() + } + + // Capture the current user + const myUserResp = await this.userCtl.getMyself() + const myUser = myUserResp[2] + + // Capture the owning company + const myCompany = this.cliUtils.getOwningCompany(this.companies) + + // Capture the current date and convert to to an ISO string + const myDate = new Date() + const myDateString = myDate.toISOString() + let studyPrototype = this._getStudyPrototype(myUser, myCompany, myDateString) + // Create the study object + let myStudies = await this._createStudyObject( + studyPrototype, + this.studyCtl + ) + + // Capture study name + const studyName = myStudies[0].name + + // Check to see if the study already exists + const foundStudy = this.cliUtils.getObject(studyName, this.studies) + if (foundStudy.length > 0) { + return [false, {status_code: 409, status_msg: `Study [${studyName}] already exists`}, null] + } + + // Catch the container for updates + let mySpinner = new ora('Preparing the repository to add studies ...') + mySpinner.start() + let repoMetadata = { + containers: { + 'Studies': {}, + }, + branch: {} + } + + const caught = await this.githubCtl.catchContainer(repoMetadata) + // Check to see if caught was successful and return an error if not + if(!caught[0]) { + return caught + } + mySpinner.stop() + + // Write the new studies + mySpinner = new ora(`Attempting to write study [${studyName}] ...`) + mySpinner.start() + + // Append the new interactions to the existing interactions + myStudies = [...myStudies, ...caught[2].containers.Studies.objects] + + // Write the new study to the backend + const createdStudies = await this.githubCtl.writeObject( + this.objectType, + myStudies, + caught[2].branch.name, + caught[2].containers.Studies.objectSha + ) + // Check to see if createdStudies was successful and return an error if not + if(!createdStudies[0]) { + return createdStudies + } + + // Release the container + mySpinner.text = (`Successfully wrote study [${studyName}], attempting to release the repository ...`) + const released = await this.githubCtl.releaseContainer(caught[2]) + mySpinner.stop() + // Return the result of the write including the interaction count and duplicate count + return [true, {status_code: 200, status_msg: `created study [${studyName}]`}, createdStudies[2]] + } + +} + +export default AddStudy \ No newline at end of file diff --git a/src/report/attic/tools.js b/src/report/attic/tools.js deleted file mode 100644 index 0e54384..0000000 --- a/src/report/attic/tools.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Common functions and tools used for report processing - * @author Michael Hay - * @file tools.js - * @copyright 2023 Mediumroast, Inc. All rights reserved. - * @license Apache-2.0 - * @version 1.0.0 - */ - -import FilesystemOperators from '../../cli/filesystem.js' - -/** - * @function getMostSimilarCompany - * @description Find the closest competitor using the Euclidean distance - * @param {Object} similarities - the similarities from a company object - * @returns {Object} An array containing a section description and a table of interaction descriptions - */ -export function getMostSimilarCompany(similarities, companies) { - // x1 = 1 and y1 = 1 because this the equivalent of comparing a company to itself - const x1 = 1 - const y1 = 1 - let distanceToCompany = {} - let companyToDistance = {} - for(const companyName in similarities) { - // Compute the distance using d = sqrt((x2 - x1)^2 + (y2 - y1)^2) - const myDistance = Math.sqrt( - (similarities[companyName].most_similar.score - x1) ** 2 + - (similarities[companyName].least_similar.score - y1) ** 2 - ) - distanceToCompany[myDistance] = companyName - companyToDistance[companyName] = myDistance - } - // Obtain the closest company using max, note min returns the least similar - const mostSimilarId = distanceToCompany[Math.max(...Object.keys(distanceToCompany))] - // Get the id for the most similar company - const mostSimilarCompany = companies.filter(company => { - if (parseInt(company.name) === parseInt(mostSimilarId)) { - return company - } - }) - // Transform the strings into floats prior to return - const allDistances = Object.keys(distanceToCompany).map ( - (distance) => { - return parseFloat(distance) - } - ) - // return both the most similar id and all computed distanceToCompany - return {mostSimilarCompany: mostSimilarCompany[0], distances: allDistances, companyMap: companyToDistance} -} - - -/** - * @function initWorkingDirs - * @description Prepare working directories for report and package creation - * @param {String} baseDir - full path to the directory to initialize with key working directories - */ -export function initWorkingDirs(baseDir) { - const fileSystem = new FilesystemOperators() - const subdirs = ['interactions', 'images'] - for(const myDir in subdirs) { - fileSystem.safeMakedir(baseDir + '/' + subdirs[myDir]) - } -} - -/** - * @function cleanWorkingDirs - * @description Clean up working directories after report and/or package creation - * @param {String} baseDir - full path to the directory to initialize with key working directories - * @returns {Array} containing the status of the rmdir operation, status message and null - */ -export function cleanWorkingDirs(baseDir) { - const fileSystem = new FilesystemOperators() - return fileSystem.rmDir(baseDir) -} \ No newline at end of file diff --git a/src/report/studies.js b/src/report/studies.js new file mode 100644 index 0000000..d3e0528 --- /dev/null +++ b/src/report/studies.js @@ -0,0 +1,248 @@ +/** + * Create reports for studies + * @author Michael Hay + * @file studies.js + * @copyright 2024 Mediumroast, Inc. All rights reserved. + * @license Apache-2.0 + * @version 1.0.0 + */ + +import ExcelJS from 'exceljs' +import Utilities from './helpers.js' + +class BaseStudyReport { + constructor(studyName, study, env) { + this.util = new Utilities(env) + this.study = study + this.studyName = studyName + this.env = env + this.baseDir = this.env.outputDir + this.workDir = this.env.workDir + this.baseName = studyName.replace(/ /g,"_") + } +} + +class SourceInsights extends BaseStudyReport { + constructor(studyName, study, env) { + super(studyName, study, env) + this.sourceData = this.study.sourceTopics + } + + async generateTop5InsightsReport() { + // Create the file name + const fileName = `${this.baseDir}/${this.baseName}_source_insights.xlsx` + // Get the largest key from sourceData + const largestKey = Object.keys(this.sourceData).reduce((a, b) => this.sourceData[a].length > this.sourceData[b].length ? a : b); + const companiesData = this.sourceData[largestKey]; + + // Create workbook + const workbook = new ExcelJS.Workbook(); + + // Iterate through each company within the largest key + for (const companyName in companiesData) { + if (companiesData.hasOwnProperty(companyName)) { + const companyData = companiesData[companyName]; + + // Count occurrences in targets + const interactionCountInTargets = {}; + for (const key in companyData) { + if (companyData.hasOwnProperty(key)) { + const item = companyData[key]; + const interaction = item.source_interaction; + + for (const target in item.targets) { + if (item.targets.hasOwnProperty(target)) { + if (!interactionCountInTargets[interaction]) { + interactionCountInTargets[interaction] = 0; + } + interactionCountInTargets[interaction]++; + } + } + } + } + + // Get top 5 interactions + const top5Interactions = Object.entries(interactionCountInTargets) + .sort((a, b) => b[1] - a[1]) + .slice(0, 5) + .map(entry => entry[0]); + + // Gather insights for top 5 interactions + const reportData = []; + for (const key in companyData) { + if (companyData.hasOwnProperty(key)) { + const item = companyData[key]; + const interaction = item.source_interaction; + + if (top5Interactions.includes(interaction)) { + const avgSimilarityScore = Object.values(item.targets).reduce((a, b) => a + b, 0) / Object.values(item.targets).length; + reportData.push({ + insight: item.insight, + source_interaction: interaction, + type: item.type, + count: item.count, + avgSimilarityScore: avgSimilarityScore, + excerpts: item.excerpts + }); + } + } + } + + // Sort report data by Count in descending order + reportData.sort((a, b) => b.count - a.count); + + // Create worksheet for the company + const worksheet = workbook.addWorksheet(companyName); + + // Add padding rows + worksheet.addRow([]); + worksheet.addRow([]); + + // Add title + worksheet.mergeCells('C3:H3'); + const titleRow = worksheet.getCell('C3'); + titleRow.value = `Top Insights Report for ${companyName}`; + titleRow.font = { size: 16, bold: true }; + titleRow.alignment = { vertical: 'middle', horizontal: 'center' }; + + // Increase the height of the row containing the title + worksheet.getRow(3).height = 30; + + // Add headers starting from C4 + worksheet.getCell('C4').value = 'Source Interaction'; + worksheet.getCell('D4').value = 'Interaction Frequency'; + worksheet.getCell('E4').value = 'Insight Text'; + worksheet.getCell('F4').value = 'Insight Type'; + worksheet.getCell('G4').value = 'Insight Avg. Similarity Score'; + worksheet.getCell('H4').value = 'Insight Excerpts'; + + // Apply styles to headers + ['C4', 'D4', 'E4', 'F4', 'G4', 'H4'].forEach(cell => { + worksheet.getCell(cell).font = { bold: true, color: { argb: 'FFFFFFFF' } }; + worksheet.getCell(cell).alignment = { vertical: 'top', horizontal: 'left' }; + worksheet.getCell(cell).fill = { + type: 'pattern', + pattern: 'solid', + fgColor: { argb: 'FF000000' } + }; + worksheet.getCell(cell).border = { + top: { style: 'thin' }, + bottom: { style: 'thin' } + }; + }); + + // Freeze row 4 + worksheet.views = [ + { state: 'frozen', ySplit: 4 } + ]; + + // Add data starting from C5 + reportData.forEach((data, index) => { + const rowIndex = index + 5; + const fillColor = index % 2 === 0 ? 'FFFFFFFF' : 'FFD3D3D3'; // Alternate row colors + + worksheet.getCell(`C${rowIndex}`).value = data.source_interaction; + worksheet.getCell(`D${rowIndex}`).value = data.count; + worksheet.getCell(`E${rowIndex}`).value = data.insight; + worksheet.getCell(`F${rowIndex}`).value = data.type; + worksheet.getCell(`G${rowIndex}`).value = data.avgSimilarityScore; + worksheet.getCell(`H${rowIndex}`).value = data.excerpts; + + // Apply styles to data cells + ['C', 'D', 'E', 'F', 'G', 'H'].forEach(col => { + const cell = worksheet.getCell(`${col}${rowIndex}`); + cell.font = { color: { argb: 'FF000000' } }; + cell.alignment = { vertical: 'top', horizontal: 'left' }; + cell.fill = { + type: 'pattern', + pattern: 'solid', + fgColor: { argb: fillColor } + }; + cell.border = { + bottom: { style: 'thin' } + }; + }); + }); + + // Set column widths + worksheet.getColumn('C').width = 50; + worksheet.getColumn('D').width = 30; + worksheet.getColumn('E').width = 70; + worksheet.getColumn('F').width = 30; + worksheet.getColumn('G').width = 30; + worksheet.getColumn('H').width = 70; + + // Wrap text in columns C (Insight) and H (Excerpts) + worksheet.getColumn('C').alignment = { wrapText: true, vertical: 'top', horizontal: 'left' }; + worksheet.getColumn('E').alignment = { wrapText: true, vertical: 'top', horizontal: 'left' }; + worksheet.getColumn('H').alignment = { wrapText: true, vertical: 'top', horizontal: 'left' }; + + // Apply autoFilter to the range + worksheet.autoFilter = { + from: 'C4', + to: 'H4' + }; + + // Apply bolded outside borders + const lastRow = worksheet.lastRow.number; + ['C', 'D', 'E', 'F', 'G', 'H'].forEach(col => { + worksheet.getCell(`${col}4`).border = { + top: { style: 'thick' }, + left: { style: 'thick' }, + bottom: { style: 'thick' }, + right: { style: 'thick' } + }; + }); + + // Apply left and right borders to the entire table + for (let rowIndex = 5; rowIndex <= lastRow; rowIndex++) { + worksheet.getCell(`C${rowIndex}`).border = { + left: { style: 'thick' }, + bottom: { style: 'thin' } + }; + worksheet.getCell(`H${rowIndex}`).border = { + right: { style: 'thick' }, + bottom: { style: 'thin' } + }; + } + + // Apply bottom borders to the last row + ['C', 'D', 'E', 'F', 'G', 'H'].forEach(col => { + worksheet.getCell(`${col}${lastRow}`).border = { + bottom: { style: 'thick' }, + }; + }); + + // Set the left and bottom border of the last cell on the left + worksheet.getCell(`C${lastRow}`).border = { + left: { style: 'thick' }, + bottom: { style: 'thick' } + }; + + // Set the right and bottom border of the last cell on the right + worksheet.getCell(`H${lastRow}`).border = { + right: { style: 'thick' }, + bottom: { style: 'thick' } + }; + + // Extend the outside border to the title cell + worksheet.getCell('C3').border = { + top: { style: 'thick' }, + left: { style: 'thick' }, + right: { style: 'thick' } + }; + worksheet.getCell('H3').border = { + top: { style: 'thick' }, + right: { style: 'thick' }, + left: { style: 'thick' } + }; + } + } + + // Write to file + const writeResult = await workbook.xlsx.writeFile(fileName); + return [true, {status_code: 200, status_msg: `Successfully wrote file to ${fileName}`}, writeResult]; + } +} + +export { SourceInsights } \ No newline at end of file