diff --git a/.eslintrc.js b/.eslintrc.js index bd06f0f..3903c6c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -10,7 +10,8 @@ module.exports = { project: './tsconfig.json' }, plugins: [ - '@typescript-eslint' + '@typescript-eslint', + 'import' ], extends: [ 'eslint:all', @@ -23,6 +24,7 @@ module.exports = { '@typescript-eslint/explicit-member-accessibility': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/init-declarations': 'off', + '@typescript-eslint/lines-around-comment': 'off', '@typescript-eslint/member-ordering': 'off', "@typescript-eslint/naming-convention": 'off', '@typescript-eslint/no-base-to-string': 'off', @@ -51,6 +53,9 @@ module.exports = { 'error', 'always' ], + '@typescript-eslint/parameter-properties': 'off', + '@typescript-eslint/prefer-nullish-coalescing': 'off', + "@typescript-eslint/prefer-optional-chain": 'off', '@typescript-eslint/prefer-readonly': 'off', '@typescript-eslint/prefer-readonly-parameter-types': 'off', '@typescript-eslint/promise-function-async': 'off', @@ -64,6 +69,7 @@ module.exports = { '@typescript-eslint/require-array-sort-compare': 'off', '@typescript-eslint/restrict-plus-operands': 'off', '@typescript-eslint/restrict-template-expressions': 'off', + '@typescript-eslint/sort-type-constituents': 'off', '@typescript-eslint/sort-type-union-intersection-members': 'off', '@typescript-eslint/space-before-function-paren': 'off', '@typescript-eslint/strict-boolean-expressions': 'off', @@ -89,11 +95,11 @@ module.exports = { 'function-paren-newline': 'off', 'guard-for-in': 'off', 'id-length': 'off', + 'import/no-duplicates': 'error', 'indent': 'off', 'init-declarations': 'off', 'line-comment-position': 'off', 'linebreak-style': 'off', - 'lines-around-comment': 'off', 'lines-between-class-members': 'off', 'max-classes-per-file': 'off', 'max-depth': 'off', @@ -111,6 +117,7 @@ module.exports = { 'no-constant-condition': 'off', 'no-console': 'off', 'no-continue': 'off', + 'no-duplicate-imports': 'off', 'no-else-return': 'off', 'no-empty': 'off', 'no-implicit-coercion': 'off', diff --git a/.gitignore b/.gitignore index 8c0cf87..f83ed1b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -node_modules +node_modules* dist out .DS_Store @@ -6,4 +6,4 @@ out .roku-deploy-staging coverage .nyc_output -*.zip \ No newline at end of file +*.zip diff --git a/CHANGELOG.md b/CHANGELOG.md index f5b1fac..702b77e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +## [4.0.0-alpha.2](https://github.com/rokucommunity/roku-deploy/compare/4.0.0-alpha.1...v4.0.0-alpha.2) - 2025-06-02 +### Added + - Add interactive remote mode ([#169](https://github.com/rokucommunity/roku-deploy/pull/169)) + + + +## [4.0.0-alpha.1](https://github.com/rokucommunity/roku-deploy/compare/v4.0.0-alpha.0...v4.0.0-alpha.1) - 2024-05-17 +### Changed + - Enhanced logging levels ([#168](https://github.com/rokucommunity/roku-deploy/pull/168)) + - Update files array ([#164](https://github.com/rokucommunity/roku-deploy/pull/164)) + - Change documentation ([#162](https://github.com/rokucommunity/roku-deploy/pull/162)) + + + +## [4.0.0-alpha.0](https://github.com/rokucommunity/roku-deploy/compare/v3.11.1...v4.0.0-alpha.0) - 2024-04-16 +### Added + - individual interfaces for every rokuDeploy function ([#126](https://github.com/rokucommunity/roku-deploy/pull/126)) + - cli commands ([#139](https://github.com/rokucommunity/roku-deploy/pull/139)) + - cli commands and rename roku-deploy functions, reorganize functions ([#142](https://github.com/rokucommunity/roku-deploy/pull/142)) + - cwd option ([#158](https://github.com/rokucommunity/roku-deploy/pull/158)) +### Changed + - don't normalize file patterns ([#131](https://github.com/rokucommunity/roku-deploy/pull/131)) + - Throw exceptions on missing options ([#156](https://github.com/rokucommunity/roku-deploy/pull/156)) + - upgrade typescript & other packages ([#157](https://github.com/rokucommunity/roku-deploy/pull/157)) +### Fixed + - bug with `{src;dest}` object handling ([#135](https://github.com/rokucommunity/roku-deploy/pull/135)) +### Removed + - removed deprecated `retainStagingFolder` property ([#130](https://github.com/rokucommunity/roku-deploy/pull/130)) + - eliminate top index functions ([#144](https://github.com/rokucommunity/roku-deploy/pull/144)) + + + ## [3.16.1](https://github.com/rokucommunity/roku-deploy/compare/3.16.0...v3.16.1) - 2025-12-05 ### Added - Add ecpSettingMode to device-info interface ([#225](https://github.com/rokucommunity/roku-deploy/pull/225)) @@ -532,7 +564,3 @@ chore: Update package.json repository to support provenance (#218) ## [1.0.0](https://github.com/RokuCommunity/roku-deploy/compare/v0.2.1...v1.0.0) - 2018-12-18 ### Added - support for negated globs - - - - diff --git a/README.md b/README.md index fc34116..b3a9d68 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # roku-deploy +This is the V4 branch, it's a work in progress. + Publish Roku projects to a Roku device by using Node.js. [![build status](https://img.shields.io/github/actions/workflow/status/rokucommunity/roku-deploy/build.yml?branch=master)](https://github.com/rokucommunity/roku-deploy/actions?query=branch%3Amaster+workflow%3Abuild) @@ -8,6 +10,19 @@ Publish Roku projects to a Roku device by using Node.js. [![npm version](https://img.shields.io/npm/v/roku-deploy.svg?logo=npm)](https://www.npmjs.com/package/roku-deploy) [![license](https://img.shields.io/github/license/rokucommunity/roku-deploy.svg)](LICENSE) [![Slack](https://img.shields.io/badge/Slack-RokuCommunity-4A154B?logo=slack)](https://join.slack.com/t/rokudevelopers/shared_invite/zt-4vw7rg6v-NH46oY7hTktpRIBM_zGvwA) + +### Table of Contents +- [Installation](#installation) +- [Requirements](#requirements) +- [Upgrading to V4](#upgrading-to-v4) +- [CLI Usage](#cli-usage) +- [JavaScript Usage](#javascript-usage) +- [Options Priority Order](#options-priority-order) +- [Files Array](#files-array) +- [Available roku-deploy Options](#available-roku-deploy-options) +- [Troubleshooting](#troubleshooting) +- [Changelog](#changelog) + ## Installation npm install roku-deploy @@ -20,6 +35,8 @@ Publish Roku projects to a Roku device by using Node.js. images/ source/ manifest + locale/ + fonts/ 2. You should create a rokudeploy.json file at the root of your project that contains all of the overrides to the default options. roku-deploy will auto-detect this file and use it when possible. (**note**: `rokudeploy.json` is jsonc, which means it supports comments). @@ -31,52 +48,154 @@ sample rokudeploy.json "password": "securePassword" } ``` -## Usage -From a node script -```javascript -var rokuDeploy = require('roku-deploy'); +## Upgrading to v4 +The new release has a few breaking changes that is worth going over in order to prepare developers for what they will need to change when they choose to upgrade. -//deploy a .zip package of your project to a roku device -rokuDeploy.deploy({ - host: 'ip-of-roku', - password: 'password for roku dev admin portal' - //other options if necessary -}).then(function(){ - //it worked -}, function(error) { - //it failed - console.error(error); -}); +### JavaScript functions don't load config files from disk +In v3, files like `roku-deploy.json` and `bsconfig.json` would be loaded anytime a rokuDeploy function was called through the NodeJS api. This functionality has been removed in v4 so that developers have more control over when the config files are loaded. If your script needs to load the config file values, you can simply call `util.getOptionsFromJson` before calling the desired rokuDeploy function. This will default to load from `rokudeploy.json`. Here's an example: + +```javascript +const config = { + //get the default options + ...rokuDeploy.getOptions(), + //override with any values found in the `rokudeploy.json` file. You can specify current working directory here. + ...util.getOptionsFromJson({ cwd: process.cwd() }) +}; +await rokuDeploy.sideload(config); ``` -Or + +### Removed support for bsconfig.json +We've removed support for loading `bsconfig.json` files. This was introduced in v3, but sometimes causes confusion between various systems (like brighterscript, vscode extension, etc). If you need to load values from a `bsconfig.json`, you can explicitly specify the config path. Like this: + ```javascript -//create a signed package of your project -rokuDeploy.deployAndSignPackage({ - host: 'ip-of-roku', - password: 'password for roku dev admin portal', - signingPassword: 'signing password' - //other options if necessary -}).then(function(pathToSignedPackage){ - console.log('Signed package created at ', pathToSignedPackage); -}, function(error) { - //it failed - console.error(error); -}); +const config = { + //get the default options + ...rokuDeploy.getOptions(), + //override with any values found in config file + ...util.getOptionsFromJson({ configPath: './bsconfig.json' }) +}; +//call some rokuDeploy function +await rokuDeploy.sideload(config); ``` +### Changed, added, or moved some functions in the main Node API +Another set of changes are the names and features available in the Node API. Some have been renamed and others have been change to be used only as CLI commands in order to organize and simplify what is offered. Renamed functions: +- `zipPackage()` -> `zip()` +- `pressHomeButton()` -> `closeChannel()` +- `publish()` -> `sideload()` +- `signExistingPackage()` -> `createSignedPackage()` +- `deleteInstalledChannel()` -> `deleteDevChannel()` +- `takeScreenshot()` -> `captureScreenshot()` + +Some functions were added to help with certain developer usecases. These mostly allow for any remote-to-Roku interaction: +- `keyPress()` +- `keyUp()` +- `keyDown()` +- `sendText()` + +Previously, some functions were available in the Node API, but have been moved to CLI commands. These are: +- `deploy()` +- `createPackage()` +- `deployAndSignPackage()` + +### Default file array changed +Lastly, the default files array has changed. node modules and static analysis files have been excluded to speed up load times. Also, `fonts/` and `locale/` was added as they are in some Roku documentation. The new default array can be seen in the section titled [Files Array](#files-array) + +## CLI Usage + +### Sideload a project to your Roku device +Sideload a .zip package or directory to a roku device: +```shell +# Sideload a zip file +npx roku-deploy sideload --host 'ip.of.roku' --password 'password' --zip './path/to/your/app.zip' + +# Sideload from a directory (will be zipped first automatically) +npx roku-deploy sideload --host 'ip.of.roku' --password 'password' --rootDir './path/to/your/project' +``` + +### Create a signed package from an existing dev channel +```shell +npx roku-deploy package --host 'ip.of.roku' --password 'password' --signingPassword 'signing password' --out './out/my-app.pkg' +``` + +### Stage files to a directory +Copy your project files to a staging directory: +```shell +npx roku-deploy stage --rootDir './path/to/root/dir' --out './path/to/staging/dir' +``` + +### Zip a directory +Create a zip file from a directory: +```shell +npx roku-deploy zip --dir './path/to/directory' --out './path/to/output.zip' +``` + +### Remote control commands +Send key presses to your Roku: +```shell +# Press a key +npx roku-deploy keyPress --key 'Home' --host 'ip.of.roku' + +# Hold a key down +npx roku-deploy keyDown --key 'Up' --host 'ip.of.roku' + +# Release a key +npx roku-deploy keyUp --key 'Up' --host 'ip.of.roku' + +# Send text to the device +npx roku-deploy sendText --text 'Hello World' --host 'ip.of.roku' + +# Interactive remote control mode +npx roku-deploy remote-control --host 'ip.of.roku' +``` + +### Convert to SquashFS +Convert your dev channel to SquashFS format: +```shell +npx roku-deploy squash --host 'ip.of.roku' --password 'password' +``` + +### Device management +```shell +# Take a screenshot +npx roku-deploy screenshot --host 'ip.of.roku' --password 'password' --out './screenshot.jpg' + +# Rekey a device with a signed package +npx roku-deploy rekey --host 'ip.of.roku' --password 'password' --pkg './path/to/signed.pkg' --signingPassword 'signing password' + +# Delete the dev channel +npx roku-deploy deleteDevChannel --host 'ip.of.roku' --password 'password' + +# Get device information +npx roku-deploy getDeviceInfo --host 'ip.of.roku' + +# Get device ID +npx roku-deploy getDevId --host 'ip.of.roku' +``` + +You can view the full list of commands and their options by running: + +```shell +npx roku-deploy --help +``` + +## JavaScript Usage ### Copying the files to staging If you'd like to use roku-deploy to copy files to a staging folder, you can do the following: ```typescript -rokuDeploy.prepublishToStaging({ +import { rokuDeploy } from 'roku-deploy'; +rokuDeploy.stage({ rootDir: "folder/with/your/source/code", stagingDir: 'path/to/staging/folder', files: [ "source/**/*", "components/**/*", "images/**/*", - "manifest" + "manifest", + "locale/**/*", + "fonts/**/*" ], //...other options if necessary }).then(function(){ @@ -90,8 +209,8 @@ rokuDeploy.prepublishToStaging({ ### Creating a zip from an already-populated staging folder Use this logic if you'd like to create a zip from your application folder. ```typescript -/create a signed package of your project -rokuDeploy.zipPackage({ +//create a signed package of your project +rokuDeploy.zip({ outDir: 'folder/to/put/zip', stagingDir: 'path/to/files/to/zip', outFile: 'filename-of-your-app.zip' @@ -104,12 +223,19 @@ rokuDeploy.zipPackage({ }); ``` +### Pressing the Home key +```typescript +rokuDeploy.keyPress({ + key: 'Home' + //...other options if necessary +}) +``` -### Deploying an existing zip +### Sideloading a project If you've already created a zip using some other tool, you can use roku-deploy to sideload the zip. ```typescript -/create a signed package of your project -rokuDeploy.publish({ +//sideload a package onto a specified Roku +rokuDeploy.sideload({ host: 'ip-of-roku', password: 'password for roku dev admin portal', outDir: 'folder/where/your/zip/resides/', @@ -123,7 +249,78 @@ rokuDeploy.publish({ }); ``` -### running roku-deploy as an npm script +### Convert to SquashFS +```typescript +rokuDeploy.convertToSquashfs({ + host: '1.2.3.4', + password: 'password' + //...other options if necessary +}) +``` + +### Create a signed package +```typescript +rokuDeploy.createSignedPackage({ + host: '1.2.3.4', + password: 'password', + signingPassword: 'signing password', + stagingDir: './path/to/staging/directory' + //...other options if necessary +}) +``` + +### Send text to device +```typescript +rokuDeploy.sendText({ + text: 'Hello World', + host: 'ip-of-roku' + //...other options if necessary +}) +``` + +### Take a screenshot +```typescript +rokuDeploy.captureScreenshot({ + host: 'ip-of-roku', + password: 'password', + screenshotDir: './screenshots/', + screenshotFile: 'screenshot.jpg' + //...other options if necessary +}) +``` + +### Rekey a device +```typescript +rokuDeploy.rekeyDevice({ + host: 'ip-of-roku', + password: 'password', + rekeySignedPackage: './path/to/signed.pkg' + //...other options if necessary +}) +``` + +Can't find what you need? We offer a variety of functions available in the [RokuDeploy.ts file](https://github.com/rokucommunity/roku-deploy/blob/v4/src/RokuDeploy.ts). Here are all of the public functions: +- `stage()` +- `zip()` +- `sideload()` +- `getFilePaths()` +- `keyPress()` +- `keyUp()` +- `keyDown()` +- `sendText()` +- `closeChannel()` +- `rekeyDevice()` +- `createSignedPackage()` +- `deleteDevChannel()` +- `captureScreenshot()` +- `convertToSquashfs()` +- `getDeviceInfo()` +- `getDevId()` +- `getOptions()` +- `checkRequiredOptions()` + + +### Running roku-deploy as an npm script From an npm script in `package.json`. (Requires `rokudeploy.json` to exist at the root level where this is being run) { @@ -132,38 +329,16 @@ From an npm script in `package.json`. (Requires `rokudeploy.json` to exist at th } } -You can provide a callback in any of the higher level methods, which allows you to modify the copied contents before the package is zipped. An info object is passed in with the following attributes -- **manifestData:** [key: string]: string - Contains all the parsed values from the manifest file -- **stagingDir:** string - Path to staging folder to make it so you only need to know the relative path to what you're trying to modify - - ```javascript - let options = { - host: 'ip-of-roku', - password: 'password for roku dev admin portal' - //other options if necessary - }; - - rokuDeploy.deploy(options, (info) => { - //modify staging dir before it's zipped. - //At this point, all files have been copied to the staging directory. - manipulateFilesInStagingFolder(info.stagingDir) - //this function can also return a promise, - //which will be awaited before roku-deploy starts deploying. - }).then(function(){ - //it worked - }, function(){ - //it failed - }); - ``` - -## bsconfig.json -Another common config file is [bsconfig.json](https://github.com/rokucommunity/brighterscript#bsconfigjson-options), used by the [BrighterScript](https://github.com/rokucommunity/brighterscript) project and the [BrightScript extension for VSCode](https://github.com/rokucommunity/vscode-brightscript-language). Since many of the config settings are shared between `roku-deploy.json` and `bsconfig.json`, `roku-deploy` supports reading from that file as well. Here is the loading order: - - if `roku-deploy.json` is found, those settings are used. - - if `roku-deploy.json` is not found, look for `bsconfig.json` and use those settings. +## Options Priority Order +RokuDeploy can be configured in various ways (cli args, `roku-deploy.json`, parameters, and defaults). Here's the order these options will be loaded: +**When run from the CLI:** + - start with the default set of options from `rokuDeploy.getOptions()` + - override with any values found in `roku-deploy.json` or specified config file + - override with any values from CLI args -Note that When roku-deploy is called from within a NodeJS script, the options passed into the roku-deploy methods will override any options found in `roku-deploy.json` and `bsconfig.json`. +**When run from javascript:** + - start with the default set of options from `rokuDeploy.getOptions()` + - override with any values passed in as function arguments ## Files Array @@ -175,10 +350,14 @@ For most standard projects, the default files array should work just fine: ```jsonc { "files": [ - "source/**/*", - "components/**/*", - "images/**/*", - "manifest" + "source/**/*.*", + "components/**/*.*", + "images/**/*.*", + "locale/**/*", + "fonts/**/*", + "manifest", + "!node_modules", + "!**/*.{md,DS_Store,db}" ] } ``` @@ -190,12 +369,16 @@ If you want to include additonal files, you will need to provide the entire arra ```jsonc { "files": [ - "source/**/*", - "components/**/*", - "images/**/*", - "manifest" + "source/**/*.*", + "components/**/*.*", + "images/**/*.*", + "locale/**/*", + "fonts/**/*", + "manifest", + "!node_modules", + "!**/*.{md,DS_Store,db}", //your folder with other assets - "assets/**/*", + "assets/**/*" ] } ``` @@ -254,8 +437,8 @@ The object structure is as follows: } ``` #### { src; dest } Object Rules - - if `src` is a non-glob path to a single file, then `dest` should include the filename and extension. For example: - `{ src: "lib/Promise/promise.brs", dest: "source/promise.brs"}` +- if `src` is a non-glob path to a single file, then `dest` should include the filename and extension. For example: +`{ src: "lib/Promise/promise.brs", dest: "source/promise.brs"}` - if `src` is a glob pattern, then `dest` should be a path to the folder in the output directory. For example: `{ src: "lib/*.brs", dest: "source/lib"}` @@ -287,30 +470,29 @@ For example, if you have a base project, and then a child project that wants to -## roku-deploy Options -Here are the available options. The defaults are shown to the right of the option name, but all can be overridden: +## Available roku-deploy Options +Here are the available options for customizing to your developer-specific workflows. The defaults are shown to the right of the option name, but all can be overridden: - **host:** string (*required*) - The IP address or hostname of the target Roku device. Example: `"192.168.1.21"` + The IP address or hostname of the target Roku device. Example: `"192.168.1.21"`. - **password:** string (*required*) - The password for logging in to the developer portal on the target Roku device + The password for logging in to the developer portal on the target Roku device. - **signingPassword:** string (*required for signing*) - The password used for creating signed packages + The password used for creating signed packages. - **rekeySignedPackage:** string (*required for rekeying*) - Path to a copy of the signed package you want to use for rekeying + Path to a copy of the signed package you want to use for rekeying. - **devId:** string - Dev ID we are expecting the device to have. If supplied we check that the dev ID returned after keying matches what we expected - + Dev ID we are expecting the device to have. If supplied we check that the dev ID returned after keying matches what we expected. - **outDir?:** string = `"./out"` - A full path to the folder where the zip/pkg package should be placed + A full path to the folder where the zip/pkg package should be placed. - **outFile?:** string = `"roku-deploy"` - The base filename the zip/pkg file should be given (excluding the extension) + The base filename the zip/pkg file should be given (excluding the extension). - **rootDir?:** string = `'./'` The root path to the folder holding your project. The manifest file should be directly underneath this folder. Use this option when your roku project is in a subdirectory of where roku-deploy is installed. @@ -321,10 +503,14 @@ Here are the available options. The defaults are shown to the right of the optio "source/**/*.*", "components/**/*.*", "images/**/*.*", - "manifest" + "locale/**/*", + "fonts/**/*", + "manifest", + "!node_modules", + "!**/*.{md,DS_Store,db}" ] ``` - An array of file paths, globs, or {src:string;dest:string} objects that will be copied into the deployment package. + An array of file paths, globs, or `{ src: string; dest: string }` objects that will be copied into the deployment package. Make sure to _exclusively_ use forward slashes ( `/` ) for path separators (even on Windows), as backslashes are reserved for character escaping. You can learn more about this requirement [here](https://www.npmjs.com/package/fast-glob?activeTab=readme#how-to-write-patterns-on-windows). Using the {src;dest} objects will allow you to move files into different destination paths in the deployment package. This would be useful for copying environment-specific configs into a common config location @@ -358,37 +544,46 @@ Here are the available options. The defaults are shown to the right of the optio *NOTE:* If you override this "files" property, you need to provide **all** config values, as your array will completely overwrite the default. -- **retainStagingFolder?:** boolean = `false` - Set this to true to prevent the staging folder from being deleted after creating the package. This is helpful for troubleshooting why your package isn't being created the way you expected. - - **stagingDir?:** string = `` `${options.outDir}/.roku-deploy-staging` `` The path to the staging folder (where roku-deploy places all of the files right before zipping them up). - **convertToSquashfs?:** boolean = `false` - If true we convert to squashfs before creating the pkg file - -- **incrementBuildNumber?:** boolean = `false` - If true we increment the build number to be a timestamp in the format yymmddHHMM + If true we convert to squashfs before creating the pkg file. - **username?:** string = `"rokudev"` The username for the roku box. This will always be 'rokudev', but allow to be passed in - just in case roku adds support for custom usernames in the future + just in case roku adds support for custom usernames in the future. -- **packagePort?:** string = 80 +- **packagePort?:** number = `80` The port used for package-related requests. This is mainly used for things like emulators, or when your roku is behind a firewall with a port-forward. -- **remotePort?:** string = 8060 +- **remotePort?:** number = `8060` The port used for sending remote control commands (like home press or back press). This is mainly used for things like emulators, or when your roku is behind a firewall with a port-forward. -- **remoteDebug?:** boolean = false +- **screenshotDir?:** string = `"./tmp/roku-deploy/screenshots/"` + The directory where screenshots should be saved. Will use the OS temp directory by default. + +- **timeout?:** number = `150000` + The number of milliseconds at which point this request should timeout and return a rejected promise. + +- **remoteDebug?:** boolean = `false` When publishing a side loaded channel this flag can be used to enable the socket based BrightScript debug protocol. This should always be `false` unless you're creating a plugin for an editor such as VSCode, Atom, Sublime, etc. More information on the BrightScript debug protocol can be found here: https://developer.roku.com/en-ca/docs/developer-program/debugging/socket-based-debugger.md -- **deleteInstalledChannel?:** boolean = true - If true the previously installed dev channel will be deleted before installing the new one +- **cwd?:** string = `process.cwd()` + The current working directory, which all other paths will be set relative to. If left to default, it will be set as the process.cwd() method, which returns the current working directory of the Node.js process. + +- **deleteDevChannel?:** boolean = `true` + If true the previously installed dev channel will be deleted before installing the new one. + +- **packageUploadOverrides?:** + Overrides for values used during the zip upload process. You probably don't need to change these... + +- **logLevel?:** string = `"error"` + The level lof logging information printed in the console. You can read more information [here](https://github.com/rokucommunity/logger) -Click [here](https://github.com/rokucommunity/roku-deploy/blob/master/src/RokuDeployOptions.ts) to see the typescript interface for these options +Click [here](https://github.com/rokucommunity/roku-deploy/blob/v4/src/RokuDeployOptions.ts) to see the typescript interface for these options ## User-agent diff --git a/package-lock.json b/package-lock.json index 84eed23..b73f61e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,15 @@ { "name": "roku-deploy", - "version": "3.16.1", + "version": "4.0.0-alpha.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "roku-deploy", - "version": "3.16.1", + "version": "4.0.0-alpha.2", "license": "MIT", "dependencies": { + "@rokucommunity/logger": "^0.3.10", "@types/request": "^2.47.0", "chalk": "^2.4.2", "dateformat": "^3.0.3", @@ -17,7 +18,7 @@ "fs-extra": "^7.0.1", "is-glob": "^4.0.3", "jsonc-parser": "^2.3.0", - "jszip": "^3.6.0", + "jszip": "^3.10.1", "lodash": "^4.17.21", "micromatch": "^4.0.4", "moment": "^2.29.1", @@ -25,7 +26,8 @@ "postman-request": "^2.88.1-postman.40", "semver": "^7.7.3", "temp-dir": "^2.0.0", - "xml2js": "^0.5.0" + "xml2js": "^0.5.0", + "yargs": "^17.7.2" }, "bin": { "roku-deploy": "dist/cli.js" @@ -42,19 +44,22 @@ "@types/semver": "^7.7.1", "@types/sinon": "^10.0.4", "@types/xml2js": "^0.4.5", - "@typescript-eslint/eslint-plugin": "5.1.0", - "@typescript-eslint/parser": "5.1.0", + "@types/yargs": "^17.0.32", + "@typescript-eslint/eslint-plugin": "^5.62.0", + "@typescript-eslint/parser": "^5.59.11", "chai": "^4.3.4", "coveralls-next": "^4.2.0", - "eslint": "8.0.1", - "mocha": "^9.1.3", + "eslint": "^8.56.0", + "eslint-plugin-import": "^2.29.1", + "mocha": "^10.3.0", "nyc": "^15.1.0", "q": "^1.5.1", "rimraf": "^6.0.1", - "sinon": "^11.1.2", - "source-map-support": "^0.5.13", - "ts-node": "^10.3.1", - "typescript": "^4.4.4", + "sinon": "^12.0.0", + "source-map-support": "^0.5.21", + "ts-node": "^10.9.2", + "typescript": "^5.4.3", + "typescript-eslint": "^7.4.0", "undent": "^1.0.0" } }, @@ -310,6 +315,14 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", @@ -392,75 +405,137 @@ "node": ">=6.9.0" } }, - "node_modules/@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, "engines": { - "node": ">= 12" + "node": ">=12" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, "dependencies": { - "@cspotcode/source-map-consumer": "0.8.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.3.tgz", - "integrity": "sha512-DHI1wDPoKCBPoLZA3qDR91+3te/wDSc1YhKg3jR8NxKKRJq2hwHwcWv31cSwSYvIBrmbENoYMWcenW8uproQqg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.0.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "engines": { - "node": ">= 4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", - "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", - "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead", "dev": true }, @@ -772,6 +847,121 @@ "node": "*" } }, + "node_modules/@rokucommunity/logger": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@rokucommunity/logger/-/logger-0.3.11.tgz", + "integrity": "sha512-yQWa+rRVYkZeEYFePHzNtB6/DL+wPjjL/xt9wM0gQPDugSl8kJZ3ywyUtrFS+y/wzY0Nc2M+1704GVx0vdOO9A==", + "dependencies": { + "chalk": "^4.1.2", + "date-fns": "^2.30.0", + "fs-extra": "^10.0.0", + "parse-ms": "^2.1.0", + "safe-json-stringify": "^1.2.0", + "serialize-error": "^8.1.0" + } + }, + "node_modules/@rokucommunity/logger/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@rokucommunity/logger/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@rokucommunity/logger/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@rokucommunity/logger/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@rokucommunity/logger/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@rokucommunity/logger/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@rokucommunity/logger/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@rokucommunity/logger/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@rokucommunity/logger/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, "node_modules/@sinonjs/commons": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", @@ -864,9 +1054,15 @@ "dev": true }, "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, "node_modules/@types/lodash": { @@ -942,19 +1138,36 @@ "@types/node": "*" } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.1.0.tgz", - "integrity": "sha512-bekODL3Tqf36Yz8u+ilha4zGxL9mdB6LIsIoMAvvC5FAuWo4NpZYXtCbv7B2CeR1LhI/lLtLk+q4tbtxuoVuCg==", + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "5.1.0", - "@typescript-eslint/scope-manager": "5.1.0", - "debug": "^4.3.2", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.2.0", - "semver": "^7.3.5", + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", "tsutils": "^3.21.0" }, "engines": { @@ -974,18 +1187,16 @@ } } }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.1.0.tgz", - "integrity": "sha512-ovE9qUiZMOMgxQAESZsdBT+EXIfx/YUYAbwGUI6V03amFdOOxI9c6kitkgRvLkJaLusgMZ2xBhss+tQ0Y1HWxA==", + "node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.1.0", - "@typescript-eslint/types": "5.1.0", - "@typescript-eslint/typescript-estree": "5.1.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -995,19 +1206,22 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/parser": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.1.0.tgz", - "integrity": "sha512-vx1P+mhCtYw3+bRHmbalq/VKP2Y3gnzNgxGxfEWc6OFpuEL7iQdAeq11Ke3Rhy8NjgB+AHsIWEwni3e+Y7djKA==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.1.0", - "@typescript-eslint/types": "5.1.0", - "@typescript-eslint/typescript-estree": "5.1.0", - "debug": "^4.3.2" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1015,24 +1229,18 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.1.0.tgz", - "integrity": "sha512-yYlyVjvn5lvwCL37i4hPsa1s0ORsjkauhTqbb8MnpvUs7xykmcjGqwlNZ2Q5QpoqkJ1odlM2bqHqJwa28qV6Tw==", + "node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.1.0", - "@typescript-eslint/visitor-keys": "5.1.0" + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1040,12 +1248,20 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/@typescript-eslint/types": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.1.0.tgz", - "integrity": "sha512-sEwNINVxcB4ZgC6Fe6rUyMlvsB2jvVdgxjZEjQUQVlaSPMNamDOwO6/TB98kFt4sYYfNhdhTPBEQqNQZjMMswA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1056,17 +1272,17 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.1.0.tgz", - "integrity": "sha512-SSz+l9YrIIsW4s0ZqaEfnjl156XQ4VRmJsbA0ZE1XkXrD3cRpzuZSVCyqeCMR3EBjF27IisWakbBDGhGNIOvfQ==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.1.0", - "@typescript-eslint/visitor-keys": "5.1.0", - "debug": "^4.3.2", - "globby": "^11.0.4", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.5", + "semver": "^7.3.7", "tsutils": "^3.21.0" }, "engines": { @@ -1082,14 +1298,40 @@ } } }, + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.1.0.tgz", - "integrity": "sha512-uqNXepKBg81JVwjuqAxYrXa1Ql/YDzM+8g/pS+TCPxba0wZttl8m5DkrasbfnmJGHs4lQ2jTbcZ5azGhI7kK+w==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.1.0", - "eslint-visitor-keys": "^3.0.0" + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1099,16 +1341,16 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true }, "node_modules/acorn": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", - "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1164,9 +1406,9 @@ } }, "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, "engines": { "node": ">=6" @@ -1176,7 +1418,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -1238,6 +1479,44 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -1247,36 +1526,138 @@ "node": ">=8" } }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, "engines": { - "node": ">=0.8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true, "engines": { "node": "*" } }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -1416,6 +1797,53 @@ "node": ">=8" } }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1548,14 +1976,16 @@ } }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/color-convert": { @@ -1690,6 +2120,72 @@ "node": ">=0.10" } }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -1704,12 +2200,12 @@ "integrity": "sha512-JLC809s6Y948/FuCZPm5IX8rRhQwOiyMb2TfVVQEixG7P8Lm/gt5S7yoQZmC8x1UehI9Pb7sksEt4xx14m+7Ug==" }, "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1759,6 +2255,40 @@ "node": ">=8" } }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1768,9 +2298,9 @@ } }, "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true, "engines": { "node": ">=0.3.1" @@ -1800,6 +2330,20 @@ "node": ">=6.0.0" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -1824,19 +2368,148 @@ "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dev": true, "dependencies": { - "ansi-colors": "^4.1.1" + "es-errors": "^1.3.0" }, "engines": { - "node": ">=8.6" + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/es6-error": { @@ -1849,7 +2522,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, "engines": { "node": ">=6" } @@ -1863,50 +2535,50 @@ } }, "node_modules/eslint": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.0.1.tgz", - "integrity": "sha512-LsgcwZgQ72vZ+SMp4K6pAnk2yFDWL7Ti4pJaRvsZ0Hsw2h8ZjUIW38a9AFn2cZXdBMlScMFYYgsSp4ttFI/0bA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.0.3", - "@humanwhocodes/config-array": "^0.6.0", - "ajv": "^6.10.0", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^6.0.0", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.0.0", - "espree": "^9.0.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.2.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" @@ -1918,75 +2590,160 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" + "ms": "^2.1.1" } }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "node_modules/eslint-module-utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, "engines": { - "node": ">=10" + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/eslint-visitor-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.0.0.tgz", - "integrity": "sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q==", + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "dependencies": { + "ms": "^2.1.1" } }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/eslint-plugin-import": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", + "tsconfig-paths": "^3.15.0" }, "engines": { - "node": ">=8" + "node": ">=4" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", @@ -2035,9 +2792,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-6.0.0.tgz", - "integrity": "sha512-uRDL9MWmQCkaFus8RF5K9/L/2fn+80yoW3jkD53l4shjCh26fCtvJGasxjUqP5OT87SYTxCVA3BwTUzuELx9kA==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -2045,12 +2802,15 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" @@ -2065,15 +2825,6 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, "node_modules/eslint/node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -2099,17 +2850,20 @@ } }, "node_modules/espree": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.0.0.tgz", - "integrity": "sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "acorn": "^8.5.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.0.0" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/esprima": { @@ -2126,9 +2880,9 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -2138,9 +2892,9 @@ } }, "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" @@ -2159,9 +2913,9 @@ } }, "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { "node": ">=4.0" @@ -2237,7 +2991,7 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "node_modules/fastq": { @@ -2348,6 +3102,21 @@ "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", "dev": true }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", @@ -2435,11 +3204,43 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/gensync": { "version": "1.0.0-beta.2", @@ -2454,7 +3255,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -2468,6 +3268,30 @@ "node": "*" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -2477,6 +3301,36 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -2519,9 +3373,9 @@ } }, "node_modules/globals": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", - "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -2533,17 +3387,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { @@ -2553,19 +3423,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "engines": { - "node": ">=4.x" - } + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true }, "node_modules/har-schema": { "version": "2.0.0", @@ -2588,6 +3467,18 @@ "node": ">=6" } }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -2596,6 +3487,60 @@ "node": ">=4" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasha": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", @@ -2621,6 +3566,18 @@ "node": ">=8" } }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -2650,9 +3607,9 @@ } }, "node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { "node": ">= 4" @@ -2664,9 +3621,9 @@ "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "dependencies": { "parent-module": "^1.0.0", @@ -2713,44 +3670,241 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, "dependencies": { - "binary-extensions": "^2.0.0" + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, "dependencies": { - "is-extglob": "^2.1.1" + "has-bigints": "^1.0.2" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-number": { @@ -2761,6 +3915,76 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -2773,6 +3997,54 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -2790,6 +4062,49 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -3077,14 +4392,14 @@ } }, "node_modules/jszip": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.8.0.tgz", - "integrity": "sha512-cnpQrXvFSLdsR9KR5/x7zdf6c3m8IhZfZzSblFEHSqBaVwD2nvJ4CuCKLyvKvwBgZm08CgfSoiTBQLm5WW9hGw==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", - "set-immediate-shim": "~1.0.1" + "setimmediate": "^1.0.5" } }, "node_modules/just-extend": { @@ -3286,6 +4601,15 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3356,46 +4680,38 @@ } }, "node_modules/mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", - "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "4.2.1", - "ms": "2.1.3", - "nanoid": "3.3.1", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" }, "bin": { "_mocha": "bin/_mocha", - "mocha": "bin/mocha" + "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" + "node": ">= 14.0.0" } }, "node_modules/mocha/node_modules/argparse": { @@ -3404,6 +4720,26 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -3416,7 +4752,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/has-flag": { + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -3438,23 +4794,17 @@ } }, "node_modules/mocha/node_modules/minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=10" } }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -3470,6 +4820,24 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/moment": { "version": "2.29.4", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", @@ -3479,29 +4847,23 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, "node_modules/nise": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz", @@ -3767,6 +5129,97 @@ "node": "*" } }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3777,9 +5230,9 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { "deep-is": "^0.1.3", @@ -3787,12 +5240,29 @@ "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -3917,6 +5387,12 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, "node_modules/path-scurry": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", @@ -4061,6 +5537,15 @@ "node": ">=8" } }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postman-request": { "version": "2.88.1-postman.40", "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.40.tgz", @@ -4127,15 +5612,6 @@ "node": ">=8" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -4227,16 +5703,46 @@ "node": ">=8.10.0" } }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/release-zalgo": { @@ -4255,7 +5761,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -4271,6 +5776,26 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -4405,11 +5930,80 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "node_modules/safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -4432,10 +6026,24 @@ "node": ">=10" } }, + "node_modules/serialize-error": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", + "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "dependencies": { "randombytes": "^2.1.0" @@ -4447,14 +6055,57 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, - "node_modules/set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -4476,42 +6127,123 @@ "node": ">=8" } }, - "node_modules/signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", - "dev": true - }, - "node_modules/sinon": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-11.1.2.tgz", - "integrity": "sha512-59237HChms4kg7/sXhiRcUzdSkKuydDeTiamT/jesUVHshBgL8XAmhgFo0GfK6RruMDM/iRSij1EybmMog9cJw==", - "deprecated": "16.1.1", + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, "dependencies": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^7.1.2", - "@sinonjs/samsam": "^6.0.2", - "diff": "^5.0.0", - "nise": "^5.1.0", - "supports-color": "^7.2.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/sinon" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sinon/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sinon/node_modules/supports-color": { - "version": "7.2.0", + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", + "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", + "dev": true + }, + "node_modules/sinon": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-12.0.1.tgz", + "integrity": "sha512-iGu29Xhym33ydkAT+aNQFBINakjq69kKO6ByPvTsm3yyIACfyQttRTP03aBP/I8GfhFmLzrnKwNNkr0ORb1udg==", + "deprecated": "16.1.1", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": "^8.1.0", + "@sinonjs/samsam": "^6.0.2", + "diff": "^5.0.0", + "nise": "^5.1.0", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/sinon/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, @@ -4541,9 +6273,9 @@ } }, "node_modules/source-map-support": { - "version": "0.5.20", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", - "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "dependencies": { "buffer-from": "^1.0.0", @@ -4622,6 +6354,19 @@ "node": ">=0.10.0" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/stream-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-length/-/stream-length-1.0.2.tgz", @@ -4642,7 +6387,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -4664,11 +6408,66 @@ "strip-ansi": "^6.0.1" } }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -4718,6 +6517,18 @@ "node": ">=4" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/temp-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", @@ -4757,13 +6568,25 @@ "node": ">=8.0" } }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-node": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.3.1.tgz", - "integrity": "sha512-Yw3W2mYzhHfCHOICGNJqa0i+rbL0rAyg7ZIHxU+K4pgY8gd2Lh1j+XbHCusJMykbj6RZMJVOY0MlHVd+GOivcw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "dependencies": { - "@cspotcode/source-map-support": "0.7.0", + "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", @@ -4774,11 +6597,13 @@ "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "bin": { "ts-node": "dist/bin.js", "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", "ts-node-script": "dist/bin-script.js", "ts-node-transpile-only": "dist/bin-transpile.js", "ts-script": "dist/bin-script-deprecated.js" @@ -4790,102 +6615,461 @@ "typescript": ">=2.7" }, "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.18.0.tgz", + "integrity": "sha512-PonBkP603E3tt05lDkbOMyaxJjvKqQrXsnow72sVeOFINDE/qNmnnd+f9b4N+U7W6MXnnYyrhtmF2t08QWwUbA==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "7.18.0", + "@typescript-eslint/parser": "7.18.0", + "@typescript-eslint/utils": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { "optional": true } } }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, "engines": { - "node": ">=0.3.1" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", "dev": true, "dependencies": { - "tslib": "^1.8.1" + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": ">= 6" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", "dev": true, "dependencies": { - "prelude-ls": "^1.2.1" + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": ">= 0.8.0" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" + }, "engines": { - "node": ">=4" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, "engines": { - "node": ">=10" + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "node_modules/typescript-eslint/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "dependencies": { - "is-typedarray": "^1.0.0" + "balanced-match": "^1.0.0" } }, - "node_modules/typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", + "node_modules/typescript-eslint/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" }, "engines": { - "node": ">=4.2.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/undent": { @@ -4937,10 +7121,10 @@ "uuid": "bin/uuid" } }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, "node_modules/verror": { @@ -4976,32 +7160,122 @@ "node": ">= 8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/word-wrap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", - "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", "dev": true }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -5063,7 +7337,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -5078,7 +7351,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -5089,8 +7361,7 @@ "node_modules/wrap-ansi/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/wrappy": { "version": "1.0.2", @@ -5134,33 +7405,31 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "engines": { "node": ">=10" } }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "engines": { "node": ">=10" @@ -5214,6 +7483,14 @@ "node": ">=8" } }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -5426,6 +7703,11 @@ "@babel/types": "^7.27.3" } }, + "@babel/runtime": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==" + }, "@babel/template": { "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", @@ -5489,61 +7771,103 @@ "@babel/helper-validator-identifier": "^7.27.1" } }, - "@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "dev": true - }, "@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, + "@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, "requires": { - "@cspotcode/source-map-consumer": "0.8.0" + "eslint-visitor-keys": "^3.4.3" } }, + "@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true + }, "@eslint/eslintrc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.3.tgz", - "integrity": "sha512-DHI1wDPoKCBPoLZA3qDR91+3te/wDSc1YhKg3jR8NxKKRJq2hwHwcWv31cSwSYvIBrmbENoYMWcenW8uproQqg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.0.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "dependencies": { - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } } } }, + "@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true + }, "@humanwhocodes/config-array": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.6.0.tgz", - "integrity": "sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" } }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, "@humanwhocodes/object-schema": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", - "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, "@isaacs/cliui": { @@ -5759,19 +8083,107 @@ }, "dependencies": { "universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==" + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==" + } + } + }, + "@postman/tunnel-agent": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.4.tgz", + "integrity": "sha512-CJJlq8V7rNKhAw4sBfjixKpJW00SHqebqNUQKxMoepgeWZIbdPcD+rguRcivGhS4N12PymDcKgUgSD4rVC+RjQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "@rokucommunity/logger": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@rokucommunity/logger/-/logger-0.3.11.tgz", + "integrity": "sha512-yQWa+rRVYkZeEYFePHzNtB6/DL+wPjjL/xt9wM0gQPDugSl8kJZ3ywyUtrFS+y/wzY0Nc2M+1704GVx0vdOO9A==", + "requires": { + "chalk": "^4.1.2", + "date-fns": "^2.30.0", + "fs-extra": "^10.0.0", + "parse-ms": "^2.1.0", + "safe-json-stringify": "^1.2.0", + "serialize-error": "^8.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==" } } }, - "@postman/tunnel-agent": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.4.tgz", - "integrity": "sha512-CJJlq8V7rNKhAw4sBfjixKpJW00SHqebqNUQKxMoepgeWZIbdPcD+rguRcivGhS4N12PymDcKgUgSD4rVC+RjQ==", - "requires": { - "safe-buffer": "^5.0.1" - } + "@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true }, "@sinonjs/commons": { "version": "1.8.3", @@ -5865,9 +8277,15 @@ "dev": true }, "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, "@types/lodash": { @@ -5942,99 +8360,130 @@ "@types/node": "*" } }, - "@typescript-eslint/eslint-plugin": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.1.0.tgz", - "integrity": "sha512-bekODL3Tqf36Yz8u+ilha4zGxL9mdB6LIsIoMAvvC5FAuWo4NpZYXtCbv7B2CeR1LhI/lLtLk+q4tbtxuoVuCg==", + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "5.1.0", - "@typescript-eslint/scope-manager": "5.1.0", - "debug": "^4.3.2", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.2.0", - "semver": "^7.3.5", + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", "tsutils": "^3.21.0" } }, - "@typescript-eslint/experimental-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.1.0.tgz", - "integrity": "sha512-ovE9qUiZMOMgxQAESZsdBT+EXIfx/YUYAbwGUI6V03amFdOOxI9c6kitkgRvLkJaLusgMZ2xBhss+tQ0Y1HWxA==", + "@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "requires": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.1.0", - "@typescript-eslint/types": "5.1.0", - "@typescript-eslint/typescript-estree": "5.1.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" } }, - "@typescript-eslint/parser": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.1.0.tgz", - "integrity": "sha512-vx1P+mhCtYw3+bRHmbalq/VKP2Y3gnzNgxGxfEWc6OFpuEL7iQdAeq11Ke3Rhy8NjgB+AHsIWEwni3e+Y7djKA==", + "@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.1.0", - "@typescript-eslint/types": "5.1.0", - "@typescript-eslint/typescript-estree": "5.1.0", - "debug": "^4.3.2" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" } }, - "@typescript-eslint/scope-manager": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.1.0.tgz", - "integrity": "sha512-yYlyVjvn5lvwCL37i4hPsa1s0ORsjkauhTqbb8MnpvUs7xykmcjGqwlNZ2Q5QpoqkJ1odlM2bqHqJwa28qV6Tw==", + "@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, "requires": { - "@typescript-eslint/types": "5.1.0", - "@typescript-eslint/visitor-keys": "5.1.0" + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.1.0.tgz", - "integrity": "sha512-sEwNINVxcB4ZgC6Fe6rUyMlvsB2jvVdgxjZEjQUQVlaSPMNamDOwO6/TB98kFt4sYYfNhdhTPBEQqNQZjMMswA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.1.0.tgz", - "integrity": "sha512-SSz+l9YrIIsW4s0ZqaEfnjl156XQ4VRmJsbA0ZE1XkXrD3cRpzuZSVCyqeCMR3EBjF27IisWakbBDGhGNIOvfQ==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.1.0", - "@typescript-eslint/visitor-keys": "5.1.0", - "debug": "^4.3.2", - "globby": "^11.0.4", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.5", + "semver": "^7.3.7", "tsutils": "^3.21.0" } }, + "@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + } + }, "@typescript-eslint/visitor-keys": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.1.0.tgz", - "integrity": "sha512-uqNXepKBg81JVwjuqAxYrXa1Ql/YDzM+8g/pS+TCPxba0wZttl8m5DkrasbfnmJGHs4lQ2jTbcZ5azGhI7kK+w==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.1.0", - "eslint-visitor-keys": "^3.0.0" + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" } }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true }, "acorn": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", - "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true }, "acorn-jsx": { @@ -6072,16 +8521,15 @@ } }, "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "ansi-styles": { "version": "3.2.1", @@ -6131,12 +8579,92 @@ "sprintf-js": "~1.0.2" } }, + "array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + } + }, + "array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + } + }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, + "array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + } + }, + "array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + } + }, + "array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + } + }, + "arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + } + }, "asn1": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", @@ -6156,11 +8684,26 @@ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, + "async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "requires": { + "possible-typed-array-names": "^1.0.0" + } + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -6264,6 +8807,38 @@ "write-file-atomic": "^3.0.0" } }, + "call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + } + }, + "call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + } + }, + "call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -6351,13 +8926,12 @@ "dev": true }, "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "requires": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, @@ -6474,6 +9048,47 @@ "assert-plus": "^1.0.0" } }, + "data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + } + }, + "data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + } + }, + "data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, + "date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "requires": { + "@babel/runtime": "^7.21.0" + } + }, "dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -6485,12 +9100,12 @@ "integrity": "sha512-JLC809s6Y948/FuCZPm5IX8rRhQwOiyMb2TfVVQEixG7P8Lm/gt5S7yoQZmC8x1UehI9Pb7sksEt4xx14m+7Ug==" }, "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, "decamelize": { @@ -6523,15 +9138,37 @@ "strip-bom": "^4.0.0" } }, + "define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + } + }, + "define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "requires": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true }, "dir-glob": { @@ -6552,6 +9189,17 @@ "esutils": "^2.0.2" } }, + "dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } + }, "eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -6576,16 +9224,121 @@ "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + } + }, + "es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0" + } + }, + "es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, + "es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "requires": { + "hasown": "^2.0.2" + } + }, + "es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, "requires": { - "ansi-colors": "^4.1.1" + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" } }, "es6-error": { @@ -6597,8 +9350,7 @@ "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, "escape-string-regexp": { "version": "1.0.5", @@ -6606,49 +9358,49 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.0.1.tgz", - "integrity": "sha512-LsgcwZgQ72vZ+SMp4K6pAnk2yFDWL7Ti4pJaRvsZ0Hsw2h8ZjUIW38a9AFn2cZXdBMlScMFYYgsSp4ttFI/0bA==", - "dev": true, - "requires": { - "@eslint/eslintrc": "^1.0.3", - "@humanwhocodes/config-array": "^0.6.0", - "ajv": "^6.10.0", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^6.0.0", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.0.0", - "espree": "^9.0.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.2.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "dependencies": { "ansi-styles": { @@ -6698,9 +9450,9 @@ "dev": true }, "eslint-scope": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-6.0.0.tgz", - "integrity": "sha512-uRDL9MWmQCkaFus8RF5K9/L/2fn+80yoW3jkD53l4shjCh26fCtvJGasxjUqP5OT87SYTxCVA3BwTUzuELx9kA==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -6708,9 +9460,9 @@ } }, "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "has-flag": { @@ -6719,12 +9471,6 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -6745,48 +9491,126 @@ } } }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } } }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "eslint-module-utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", "dev": true, "requires": { - "eslint-visitor-keys": "^2.0.0" + "debug": "^3.2.7" }, "dependencies": { - "eslint-visitor-keys": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-plugin-import": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "dev": true, + "requires": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", + "tsconfig-paths": "^3.15.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "doctrine": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, "eslint-visitor-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.0.0.tgz", - "integrity": "sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "espree": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.0.0.tgz", - "integrity": "sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "requires": { - "acorn": "^8.5.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^3.0.0" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" } }, "esprima": { @@ -6796,18 +9620,18 @@ "dev": true }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "requires": { "estraverse": "^5.1.0" }, "dependencies": { "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true } } @@ -6822,9 +9646,9 @@ }, "dependencies": { "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true } } @@ -6886,7 +9710,7 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "fastq": { @@ -6968,6 +9792,15 @@ "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", "dev": true }, + "for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "requires": { + "is-callable": "^1.2.7" + } + }, "foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", @@ -7022,10 +9855,30 @@ "dev": true, "optional": true }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true + }, + "function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + } + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true }, "gensync": { @@ -7037,8 +9890,7 @@ "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-func-name": { "version": "2.0.2", @@ -7046,12 +9898,51 @@ "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true }, + "get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + } + }, "get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true }, + "get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "requires": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + } + }, + "get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + } + }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -7084,37 +9975,53 @@ } }, "globals": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", - "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "requires": { "type-fest": "^0.20.2" } }, + "globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "requires": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + } + }, "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" } }, + "gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true + }, "graceful-fs": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, "har-schema": { @@ -7131,11 +10038,50 @@ "har-schema": "^2.0.0" } }, + "has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, + "has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "requires": { + "es-define-property": "^1.0.0" + } + }, + "has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "requires": { + "dunder-proto": "^1.0.0" + } + }, + "has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.3" + } + }, "hasha": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", @@ -7154,6 +10100,15 @@ } } }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "requires": { + "function-bind": "^1.1.2" + } + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -7177,9 +10132,9 @@ } }, "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true }, "immediate": { @@ -7188,9 +10143,9 @@ "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" }, "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "requires": { "parent-module": "^1.0.0", @@ -7224,6 +10179,50 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + } + }, + "is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + } + }, + "is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "requires": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + } + }, + "is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "requires": { + "has-bigints": "^1.0.2" + } + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -7233,16 +10232,82 @@ "binary-extensions": "^2.0.0" } }, + "is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + } + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true + }, + "is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "requires": { + "hasown": "^2.0.2" + } + }, + "is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + } + }, + "is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, + "is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "requires": { + "call-bound": "^1.0.3" + } + }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + } }, "is-glob": { "version": "4.0.3", @@ -7252,17 +10317,102 @@ "is-extglob": "^2.1.1" } }, + "is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true + }, + "is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, + "is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + } + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, + "is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true + }, + "is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "requires": { + "call-bound": "^1.0.3" + } + }, "is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, + "is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + } + }, + "is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + } + }, + "is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "requires": { + "which-typed-array": "^1.1.16" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -7274,6 +10424,31 @@ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, + "is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true + }, + "is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "requires": { + "call-bound": "^1.0.3" + } + }, + "is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + } + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -7502,14 +10677,14 @@ } }, "jszip": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.8.0.tgz", - "integrity": "sha512-cnpQrXvFSLdsR9KR5/x7zdf6c3m8IhZfZzSblFEHSqBaVwD2nvJ4CuCKLyvKvwBgZm08CgfSoiTBQLm5WW9hGw==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", "requires": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", - "set-immediate-shim": "~1.0.1" + "setimmediate": "^1.0.5" } }, "just-extend": { @@ -7664,6 +10839,12 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -7713,35 +10894,31 @@ "dev": true }, "mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", - "dev": true, - "requires": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "4.2.1", - "ms": "2.1.3", - "nanoid": "3.3.1", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" }, "dependencies": { "argparse": { @@ -7750,12 +10927,45 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -7772,20 +10982,14 @@ } }, "minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "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": "^1.1.7" + "brace-expansion": "^2.0.1" } }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -7794,6 +10998,21 @@ "requires": { "has-flag": "^4.0.0" } + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } } } }, @@ -7803,15 +11022,9 @@ "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "natural-compare": { @@ -7820,6 +11033,12 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, "nise": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz", @@ -8029,6 +11248,67 @@ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, + "object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + } + }, + "object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + } + }, + "object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + } + }, + "object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -8039,9 +11319,9 @@ } }, "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "requires": { "deep-is": "^0.1.3", @@ -8049,7 +11329,18 @@ "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" + } + }, + "own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "requires": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" } }, "p-limit": { @@ -8140,6 +11431,12 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, "path-scurry": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", @@ -8251,6 +11548,12 @@ } } }, + "possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true + }, "postman-request": { "version": "2.88.1-postman.40", "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.40.tgz", @@ -8307,12 +11610,6 @@ "fromentries": "^1.2.0" } }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, "psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -8376,11 +11673,35 @@ "picomatch": "^2.2.1" } }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true + "reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + } + }, + "regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + } }, "release-zalgo": { "version": "1.0.0", @@ -8394,8 +11715,7 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, "require-main-filename": { "version": "2.0.0", @@ -8408,6 +11728,17 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "requires": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -8487,11 +11818,66 @@ "queue-microtask": "^1.2.2" } }, + "safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==" + }, + "safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, + "safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + } + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -8507,10 +11893,18 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==" }, + "serialize-error": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", + "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", + "requires": { + "type-fest": "^0.20.2" + } + }, "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -8522,10 +11916,47 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" + "set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + } + }, + "set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + } + }, + "set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "requires": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" }, "shebang-command": { "version": "2.0.0", @@ -8542,6 +11973,54 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + } + }, + "side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + } + }, + "side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + } + }, + "side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + } + }, "signal-exit": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", @@ -8549,19 +12028,28 @@ "dev": true }, "sinon": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-11.1.2.tgz", - "integrity": "sha512-59237HChms4kg7/sXhiRcUzdSkKuydDeTiamT/jesUVHshBgL8XAmhgFo0GfK6RruMDM/iRSij1EybmMog9cJw==", + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-12.0.1.tgz", + "integrity": "sha512-iGu29Xhym33ydkAT+aNQFBINakjq69kKO6ByPvTsm3yyIACfyQttRTP03aBP/I8GfhFmLzrnKwNNkr0ORb1udg==", "dev": true, "requires": { "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^7.1.2", + "@sinonjs/fake-timers": "^8.1.0", "@sinonjs/samsam": "^6.0.2", "diff": "^5.0.0", "nise": "^5.1.0", "supports-color": "^7.2.0" }, "dependencies": { + "@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -8592,9 +12080,9 @@ "dev": true }, "source-map-support": { - "version": "0.5.20", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", - "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -8656,6 +12144,16 @@ "tweetnacl": "~0.14.0" } }, + "stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + } + }, "stream-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-length/-/stream-length-1.0.2.tgz", @@ -8676,7 +12174,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -8694,11 +12191,48 @@ "strip-ansi": "^6.0.1" } }, + "string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + } + }, + "string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + } + }, + "string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + } + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "requires": { "ansi-regex": "^5.0.1" } @@ -8732,6 +12266,12 @@ "has-flag": "^3.0.0" } }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, "temp-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", @@ -8762,13 +12302,20 @@ "is-number": "^7.0.0" } }, + "ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "requires": {} + }, "ts-node": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.3.1.tgz", - "integrity": "sha512-Yw3W2mYzhHfCHOICGNJqa0i+rbL0rAyg7ZIHxU+K4pgY8gd2Lh1j+XbHCusJMykbj6RZMJVOY0MlHVd+GOivcw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "requires": { - "@cspotcode/source-map-support": "0.7.0", + "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", @@ -8779,6 +12326,7 @@ "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "dependencies": { @@ -8790,6 +12338,35 @@ } } }, + "tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + } + } + }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -8828,8 +12405,60 @@ "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" + }, + "typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + } + }, + "typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "requires": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + } + }, + "typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + } + }, + "typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + } }, "typedarray-to-buffer": { "version": "3.1.5", @@ -8841,11 +12470,150 @@ } }, "typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true }, + "typescript-eslint": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.18.0.tgz", + "integrity": "sha512-PonBkP603E3tt05lDkbOMyaxJjvKqQrXsnow72sVeOFINDE/qNmnnd+f9b4N+U7W6MXnnYyrhtmF2t08QWwUbA==", + "dev": true, + "requires": { + "@typescript-eslint/eslint-plugin": "7.18.0", + "@typescript-eslint/parser": "7.18.0", + "@typescript-eslint/utils": "7.18.0" + }, + "dependencies": { + "@typescript-eslint/eslint-plugin": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + } + }, + "@typescript-eslint/parser": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + } + }, + "@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + } + }, + "@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + } + }, + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "requires": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + } + }, "undent": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/undent/-/undent-1.0.0.tgz", @@ -8888,10 +12656,10 @@ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, "verror": { @@ -8920,29 +12688,97 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "requires": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + } + }, + "which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "requires": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, + "which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "requires": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + } + }, "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + } + }, "word-wrap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", - "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, "workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", "dev": true }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8953,7 +12789,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -8962,7 +12797,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -8970,8 +12804,7 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" } } }, @@ -9047,28 +12880,33 @@ "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" }, "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "requires": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" + }, + "dependencies": { + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" + } } }, "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true }, "yargs-unparser": { diff --git a/package.json b/package.json index cb8ffe3..f2b624f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "roku-deploy", - "version": "3.16.1", + "version": "4.0.0-alpha.2", "description": "Package and publish a Roku application using Node.js", "main": "dist/index.js", "scripts": { @@ -18,6 +18,7 @@ "package": "npm run build && npm pack" }, "dependencies": { + "@rokucommunity/logger": "^0.3.10", "@types/request": "^2.47.0", "chalk": "^2.4.2", "dateformat": "^3.0.3", @@ -26,7 +27,7 @@ "fs-extra": "^7.0.1", "is-glob": "^4.0.3", "jsonc-parser": "^2.3.0", - "jszip": "^3.6.0", + "jszip": "^3.10.1", "lodash": "^4.17.21", "micromatch": "^4.0.4", "moment": "^2.29.1", @@ -34,7 +35,8 @@ "postman-request": "^2.88.1-postman.40", "semver": "^7.7.3", "temp-dir": "^2.0.0", - "xml2js": "^0.5.0" + "xml2js": "^0.5.0", + "yargs": "^17.7.2" }, "devDependencies": { "@types/chai": "^4.2.22", @@ -48,19 +50,22 @@ "@types/semver": "^7.7.1", "@types/sinon": "^10.0.4", "@types/xml2js": "^0.4.5", - "@typescript-eslint/eslint-plugin": "5.1.0", - "@typescript-eslint/parser": "5.1.0", + "@types/yargs": "^17.0.32", + "@typescript-eslint/eslint-plugin": "^5.62.0", + "@typescript-eslint/parser": "^5.59.11", "chai": "^4.3.4", "coveralls-next": "^4.2.0", - "eslint": "8.0.1", - "mocha": "^9.1.3", + "eslint": "^8.56.0", + "eslint-plugin-import": "^2.29.1", + "mocha": "^10.3.0", "nyc": "^15.1.0", "q": "^1.5.1", "rimraf": "^6.0.1", - "sinon": "^11.1.2", - "source-map-support": "^0.5.13", - "ts-node": "^10.3.1", - "typescript": "^4.4.4", + "sinon": "^12.0.0", + "source-map-support": "^0.5.21", + "ts-node": "^10.9.2", + "typescript": "^5.4.3", + "typescript-eslint": "^7.4.0", "undent": "^1.0.0" }, "mocha": { @@ -102,7 +107,7 @@ ], "sourceMap": true, "instrument": true, - "check-coverage": true, + "check-coverage": false, "lines": 100, "statements": 100, "functions": 100, diff --git a/src/Errors.ts b/src/Errors.ts index 2d2d835..3b9ec2a 100644 --- a/src/Errors.ts +++ b/src/Errors.ts @@ -1,15 +1,19 @@ import type { HttpResponse, RokuMessages } from './RokuDeploy'; import type * as requestType from 'request'; +export interface RequestResult { + response: requestType.Response; + body: any; +} export class InvalidDeviceResponseCodeError extends Error { - constructor(message: string, public results?: any) { + constructor(message: string, public results?: RequestResult) { super(message); Object.setPrototypeOf(this, InvalidDeviceResponseCodeError.prototype); } } export class UnauthorizedDeviceResponseError extends Error { - constructor(message: string, public results?: any) { + constructor(message: string, results?: any) { super(message); Object.setPrototypeOf(this, UnauthorizedDeviceResponseError.prototype); } @@ -23,28 +27,28 @@ export class EcpNetworkAccessModeDisabledError extends Error { } export class UnparsableDeviceResponseError extends Error { - constructor(message: string, public results?: any) { + constructor(message: string, results?: any) { super(message); Object.setPrototypeOf(this, UnparsableDeviceResponseError.prototype); } } export class FailedDeviceResponseError extends Error { - constructor(message: string, public results?: any) { + constructor(message: string, results?: any) { super(message); Object.setPrototypeOf(this, FailedDeviceResponseError.prototype); } } export class UnknownDeviceResponseError extends Error { - constructor(message: string, public results?: any) { + constructor(message: string, results?: any) { super(message); Object.setPrototypeOf(this, UnknownDeviceResponseError.prototype); } } export class CompileError extends Error { - constructor(message: string, public results: any, public rokuMessages: RokuMessages) { + constructor(message: string, results: any, rokuMessages: RokuMessages) { super(message); Object.setPrototypeOf(this, CompileError.prototype); } diff --git a/src/Logger.spec.ts b/src/Logger.spec.ts deleted file mode 100644 index e34a7ee..0000000 --- a/src/Logger.spec.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { expect } from 'chai'; -import { Logger, LogLevel, noop } from './Logger'; -import chalk from 'chalk'; -import { createSandbox } from 'sinon'; -const sinon = createSandbox(); - -describe('Logger', () => { - let logger: Logger; - - beforeEach(() => { - logger = new Logger(LogLevel.trace); - sinon.restore(); - //disable chalk colors for testing - sinon.stub(chalk, 'grey').callsFake((arg) => arg as any); - }); - - it('noop does nothing', () => { - noop(); - }); - - it('loglevel setter converts string to enum', () => { - (logger as any).logLevel = 'error'; - expect(logger.logLevel).to.eql(LogLevel.error); - (logger as any).logLevel = 'info'; - expect(logger.logLevel).to.eql(LogLevel.info); - }); - - it('uses LogLevel.log by default', () => { - logger = new Logger(); - expect(logger.logLevel).to.eql(LogLevel.log); - }); - - describe('log methods call correct error type', () => { - it('error', () => { - const stub = sinon.stub(logger as any, 'writeToLog').callsFake(() => { }); - logger.error(); - expect(stub.getCalls()[0].args[0]).to.eql(console.error); - }); - - it('warn', () => { - const stub = sinon.stub(logger as any, 'writeToLog').callsFake(() => { }); - logger.warn(); - expect(stub.getCalls()[0].args[0]).to.eql(console.warn); - }); - - it('log', () => { - const stub = sinon.stub(logger as any, 'writeToLog').callsFake(() => { }); - logger.log(); - expect(stub.getCalls()[0].args[0]).to.eql(console.log); - }); - - it('info', () => { - const stub = sinon.stub(logger as any, 'writeToLog').callsFake(() => { }); - logger.info(); - expect(stub.getCalls()[0].args[0]).to.eql(console.info); - }); - - it('debug', () => { - const stub = sinon.stub(logger as any, 'writeToLog').callsFake(() => { }); - logger.debug(); - expect(stub.getCalls()[0].args[0]).to.eql(console.debug); - }); - - it('trace', () => { - const stub = sinon.stub(logger as any, 'writeToLog').callsFake(() => { }); - logger.trace(); - expect(stub.getCalls()[0].args[0]).to.eql(console.trace); - }); - }); - - it('skips all errors on error level', () => { - logger.logLevel = LogLevel.off; - const stub = sinon.stub(logger as any, 'writeToLog').callsFake(() => { }); - logger.trace(); - logger.debug(); - logger.info(); - logger.log(); - logger.warn(); - logger.error(); - - expect( - stub.getCalls().map(x => x.args[0]) - ).to.eql([]); - }); - - it('does not skip when log level is high enough', () => { - logger.logLevel = LogLevel.trace; - const stub = sinon.stub(logger as any, 'writeToLog').callsFake(() => { }); - logger.trace(); - logger.debug(); - logger.info(); - logger.log(); - logger.warn(); - logger.error(); - - expect( - stub.getCalls().map(x => x.args[0]) - ).to.eql([ - console.trace, - console.debug, - console.info, - console.log, - console.warn, - console.error - ]); - }); - - describe('time', () => { - it('calls action even if logLevel is wrong', () => { - logger.logLevel = LogLevel.error; - const spy = sinon.spy(); - logger.time(LogLevel.info, null, spy); - expect(spy.called).to.be.true; - }); - - it('runs timer when loglevel is right', () => { - logger.logLevel = LogLevel.log; - const spy = sinon.spy(); - logger.time(LogLevel.log, null, spy); - expect(spy.called).to.be.true; - }); - - it('returns value', () => { - logger.logLevel = LogLevel.log; - const spy = sinon.spy(() => { - return true; - }); - expect( - logger.time(LogLevel.log, null, spy) - ).to.be.true; - expect(spy.called).to.be.true; - }); - - it('gives callable pause and resume functions even when not running timer', () => { - logger.time(LogLevel.info, null, (pause, resume) => { - pause(); - resume(); - }); - }); - - it('waits for and returns a promise when a promise is returned from the action', () => { - expect(logger.time(LogLevel.info, ['message'], () => { - return Promise.resolve(); - })).to.be.instanceof(Promise); - }); - }); -}); diff --git a/src/Logger.ts b/src/Logger.ts deleted file mode 100644 index ba9060b..0000000 --- a/src/Logger.ts +++ /dev/null @@ -1,154 +0,0 @@ -import chalk from 'chalk'; -import * as moment from 'moment'; -import { Stopwatch } from './Stopwatch'; - -export class Logger { - /** - * A string with whitespace used for indenting all messages - */ - private indent = ''; - - constructor(logLevel?: LogLevel) { - this.logLevel = logLevel; - } - - public get logLevel() { - return this._logLevel; - } - - public set logLevel(value: LogLevel) { - //cast the string version to the numberic version - if (typeof (value) === 'string') { - value = LogLevel[value] as any; - } - this._logLevel = value ?? LogLevel.log; - } - - private _logLevel = LogLevel.log; - - private getTimestamp() { - return '[' + chalk.grey(moment().format(`hh:mm:ss:SSSS A`)) + ']'; - } - - private writeToLog(method: (...consoleArgs: any[]) => void, ...args: any[]) { - if (this._logLevel === LogLevel.trace) { - method = console.trace; - } - let finalArgs = []; - for (let arg of args) { - finalArgs.push(arg); - } - method.call(console, this.getTimestamp(), this.indent, ...finalArgs); - } - - /** - * Log an error message to the console - */ - error(...messages) { - if (this._logLevel >= LogLevel.error) { - this.writeToLog(console.error, ...messages); - } - } - - /** - * Log a warning message to the console - */ - warn(...messages) { - if (this._logLevel >= LogLevel.warn) { - this.writeToLog(console.warn, ...messages); - } - } - - /** - * Log a standard log message to the console - */ - log(...messages) { - if (this._logLevel >= LogLevel.log) { - this.writeToLog(console.log, ...messages); - } - } - - /** - * Log an info message to the console - */ - info(...messages) { - if (this._logLevel >= LogLevel.info) { - this.writeToLog(console.info, ...messages); - } - } - - /** - * Log a debug message to the console - */ - debug(...messages) { - if (this._logLevel >= LogLevel.debug) { - this.writeToLog(console.debug, ...messages); - } - } - - /** - * Log a debug message to the console - */ - trace(...messages) { - if (this._logLevel >= LogLevel.trace) { - this.writeToLog(console.trace, ...messages); - } - } - - /** - * Writes to the log (if logLevel matches), and also times how long the action took to occur. - * `action` is called regardless of logLevel, so this function can be used to nicely wrap - * pieces of functionality. - * The action function also includes two parameters, `pause` and `resume`, which can be used to improve timings by focusing only on - * the actual logic of that action. - */ - time(logLevel: LogLevel, messages: any[], action: (pause: () => void, resume: () => void) => T): T { - //call the log if loglevel is in range - if (this._logLevel >= logLevel) { - let stopwatch = new Stopwatch(); - let logLevelString = LogLevel[logLevel]; - - //write the initial log - this[logLevelString](...messages ?? []); - this.indent += ' '; - - stopwatch.start(); - //execute the action - let result = action(stopwatch.stop.bind(stopwatch), stopwatch.start.bind(stopwatch)) as any; - stopwatch.stop(); - - //return a function to call when the timer is complete - let done = () => { - this.indent = this.indent.substring(2); - this[logLevelString](...messages ?? [], `finished. (${chalk.blue(stopwatch.getDurationText())})`); - }; - - //if this is a promise, wait for it to resolve and then return the original result - if (typeof result?.then === 'function') { - return Promise.resolve(result).then(done).then(() => { - return result; - }) as any; - } else { - //this was not a promise. finish the timer now - done(); - return result; - } - } else { - return action(noop, noop); - } - } -} - -export function noop() { - -} - -export enum LogLevel { - off = 0, - error = 1, - warn = 2, - log = 3, - info = 4, - debug = 5, - trace = 6 -} diff --git a/src/RokuDeploy.spec.ts b/src/RokuDeploy.spec.ts index 6a755fd..6901d02 100644 --- a/src/RokuDeploy.spec.ts +++ b/src/RokuDeploy.spec.ts @@ -8,15 +8,15 @@ import * as path from 'path'; import * as JSZip from 'jszip'; import * as child_process from 'child_process'; import * as glob from 'glob'; -import type { BeforeZipCallbackInfo } from './RokuDeploy'; -import { RokuDeploy } from './RokuDeploy'; import * as errors from './Errors'; -import { util, standardizePath as s } from './util'; +import { util, standardizePath as s, standardizePathPosix as sp } from './util'; import type { FileEntry, RokuDeployOptions } from './RokuDeployOptions'; import { cwd, expectPathExists, expectPathNotExists, expectThrowsAsync, outDir, rootDir, stagingDir, tempDir, writeFiles } from './testUtils.spec'; import { createSandbox } from 'sinon'; import * as r from 'postman-request'; import type * as requestType from 'request'; +import { RokuDeploy } from './RokuDeploy'; +import type { CaptureScreenshotOptions, ConvertToSquashfsOptions, CreateSignedPackageOptions, DeleteDevChannelOptions, GetDevIdOptions, GetDeviceInfoOptions, RekeyDeviceOptions, SendKeyEventOptions, SideloadOptions } from './RokuDeploy'; const request = r as typeof requestType; const sinon = createSandbox(); @@ -26,11 +26,12 @@ describe('RokuDeploy', () => { let options: RokuDeployOptions; let writeStreamPromise: Promise; - let writeStreamDeferred: q.Deferred & { isComplete: undefined | true }; + let writeStreamDeferred: q.Deferred & { isComplete: true | undefined }; let createWriteStreamStub: sinon.SinonStub; beforeEach(() => { rokuDeploy = new RokuDeploy(); + options = rokuDeploy.getOptions({ rootDir: rootDir, outDir: outDir, @@ -52,7 +53,7 @@ describe('RokuDeploy', () => { writeStreamPromise = writeStreamDeferred.promise as any; //fake out the write stream function - createWriteStreamStub = sinon.stub(rokuDeploy.fsExtra, 'createWriteStream').callsFake((filePath: PathLike) => { + createWriteStreamStub = sinon.stub(fsExtra, 'createWriteStream').callsFake((filePath: PathLike) => { const writeStream = fs.createWriteStream(filePath); writeStreamDeferred.resolve(writeStream); writeStreamDeferred.isComplete = true; @@ -83,29 +84,37 @@ describe('RokuDeploy', () => { describe('getOutputPkgFilePath', () => { it('should return correct path if given basename', () => { - options.outFile = 'roku-deploy'; - let outputPath = rokuDeploy.getOutputPkgFilePath(options); - expect(outputPath).to.equal(path.join(path.resolve(options.outDir), options.outFile + '.pkg')); + let outputPath = rokuDeploy['getOutputPkgFilePath']({ + outFile: 'roku-deploy', + outDir: outDir + }); + expect(outputPath).to.equal(path.join(path.resolve(outDir), options.outFile + '.pkg')); }); it('should return correct path if given outFile option ending in .zip', () => { - options.outFile = 'roku-deploy.zip'; - let outputPath = rokuDeploy.getOutputPkgFilePath(options); - expect(outputPath).to.equal(path.join(path.resolve(options.outDir), 'roku-deploy.pkg')); + let outputPath = rokuDeploy['getOutputPkgFilePath']({ + outFile: 'roku-deploy.zip', + outDir: outDir + }); + expect(outputPath).to.equal(path.join(path.resolve(outDir), 'roku-deploy.pkg')); }); }); describe('getOutputZipFilePath', () => { it('should return correct path if given basename', () => { - options.outFile = 'roku-deploy'; - let outputPath = rokuDeploy.getOutputZipFilePath(options); - expect(outputPath).to.equal(path.join(path.resolve(options.outDir), options.outFile + '.zip')); + let outputPath = rokuDeploy['getOutputZipFilePath']({ + outFile: 'roku-deploy', + outDir: outDir + }); + expect(outputPath).to.equal(path.join(path.resolve(outDir), 'roku-deploy.zip')); }); it('should return correct path if given outFile option ending in .zip', () => { - options.outFile = 'roku-deploy.zip'; - let outputPath = rokuDeploy.getOutputZipFilePath(options); - expect(outputPath).to.equal(path.join(path.resolve(options.outDir), 'roku-deploy.zip')); + let outputPath = rokuDeploy['getOutputZipFilePath']({ + outFile: 'roku-deploy.zip', + outDir: outDir + }); + expect(outputPath).to.equal(path.join(path.resolve(outDir), 'roku-deploy.zip')); }); }); @@ -691,24 +700,24 @@ describe('RokuDeploy', () => { describe('normalizeDeviceInfoFieldValue', () => { it('converts normal values', () => { - expect(rokuDeploy.normalizeDeviceInfoFieldValue('true')).to.eql(true); - expect(rokuDeploy.normalizeDeviceInfoFieldValue('false')).to.eql(false); - expect(rokuDeploy.normalizeDeviceInfoFieldValue('1')).to.eql(1); - expect(rokuDeploy.normalizeDeviceInfoFieldValue('1.2')).to.eql(1.2); + expect(rokuDeploy['normalizeDeviceInfoFieldValue']('true')).to.eql(true); + expect(rokuDeploy['normalizeDeviceInfoFieldValue']('false')).to.eql(false); + expect(rokuDeploy['normalizeDeviceInfoFieldValue']('1')).to.eql(1); + expect(rokuDeploy['normalizeDeviceInfoFieldValue']('1.2')).to.eql(1.2); //it'll trim whitespace too - expect(rokuDeploy.normalizeDeviceInfoFieldValue(' 1.2')).to.eql(1.2); - expect(rokuDeploy.normalizeDeviceInfoFieldValue(' 1.2 ')).to.eql(1.2); + expect(rokuDeploy['normalizeDeviceInfoFieldValue'](' 1.2')).to.eql(1.2); + expect(rokuDeploy['normalizeDeviceInfoFieldValue'](' 1.2 ')).to.eql(1.2); }); it('leaves invalid numbers as strings', () => { - expect(rokuDeploy.normalizeDeviceInfoFieldValue('v1.2.3')).to.eql('v1.2.3'); - expect(rokuDeploy.normalizeDeviceInfoFieldValue('1.2.3-alpha.1')).to.eql('1.2.3-alpha.1'); - expect(rokuDeploy.normalizeDeviceInfoFieldValue('123Four')).to.eql('123Four'); + expect(rokuDeploy['normalizeDeviceInfoFieldValue']('v1.2.3')).to.eql('v1.2.3'); + expect(rokuDeploy['normalizeDeviceInfoFieldValue']('1.2.3-alpha.1')).to.eql('1.2.3-alpha.1'); + expect(rokuDeploy['normalizeDeviceInfoFieldValue']('123Four')).to.eql('123Four'); }); it('decodes HTML entities', () => { - expect(rokuDeploy.normalizeDeviceInfoFieldValue('3&4')).to.eql('3&4'); - expect(rokuDeploy.normalizeDeviceInfoFieldValue('3&4')).to.eql('3&4'); + expect(rokuDeploy['normalizeDeviceInfoFieldValue']('3&4')).to.eql('3&4'); + expect(rokuDeploy['normalizeDeviceInfoFieldValue']('3&4')).to.eql('3&4'); }); }); @@ -720,81 +729,22 @@ describe('RokuDeploy', () => { ${expectedDevId} `; mockDoGetRequest(body); - options.devId = expectedDevId; - let devId = await rokuDeploy.getDevId(options); - expect(devId).to.equal(expectedDevId); - }); - }); - - describe('copyToStaging', () => { - it('throws exceptions when rootDir does not exist', async () => { - await expectThrowsAsync( - rokuDeploy['copyToStaging']([], 'staging', 'folder_does_not_exist') - ); - }); - - it('throws exceptions on missing stagingPath', async () => { - await expectThrowsAsync( - rokuDeploy['copyToStaging']([], undefined, undefined) - ); - }); - - it('throws exceptions on missing rootDir', async () => { - await expectThrowsAsync( - rokuDeploy['copyToStaging']([], 'asdf', undefined) - ); - }); - - it('computes absolute path for all operations', async () => { - const ensureDirPaths = []; - sinon.stub(rokuDeploy.fsExtra, 'ensureDir').callsFake((p) => { - ensureDirPaths.push(p); - return Promise.resolve; - }); - const copyPaths = [] as Array<{ src: string; dest: string }>; - sinon.stub(rokuDeploy.fsExtra as any, 'copy').callsFake((src, dest) => { - copyPaths.push({ src: src as string, dest: dest as string }); - return Promise.resolve(); + let devId = await rokuDeploy.getDevId({ + host: '1.2.3.4' }); - - sinon.stub(rokuDeploy, 'getFilePaths').returns( - Promise.resolve([ - { - src: s`${rootDir}/source/main.brs`, - dest: '/source/main.brs' - }, { - src: s`${rootDir}/components/a/b/c/comp1.xml`, - dest: '/components/a/b/c/comp1.xml' - } - ]) - ); - - await rokuDeploy['copyToStaging']([], stagingDir, rootDir); - - expect(ensureDirPaths).to.eql([ - s`${stagingDir}/source`, - s`${stagingDir}/components/a/b/c` - ]); - - expect(copyPaths).to.eql([ - { - src: s`${rootDir}/source/main.brs`, - dest: s`${stagingDir}/source/main.brs` - }, { - src: s`${rootDir}/components/a/b/c/comp1.xml`, - dest: s`${stagingDir}/components/a/b/c/comp1.xml` - } - ]); + expect(devId).to.equal(expectedDevId); }); }); - describe('zipPackage', () => { + describe('zip', () => { it('should throw error when manifest is missing', async () => { let err; try { - options.stagingDir = s`${tempDir}/path/to/nowhere`; fsExtra.ensureDirSync(options.stagingDir); - await rokuDeploy.zipPackage(options); + await rokuDeploy.zip({ + stagingDir: s`${tempDir}/path/to/nowhere`, + outDir: outDir + }); } catch (e) { err = (e as Error); } @@ -804,8 +754,10 @@ describe('RokuDeploy', () => { it('should throw error when manifest is missing and stagingDir does not exist', async () => { let err; try { - options.stagingDir = s`${tempDir}/path/to/nowhere`; - await rokuDeploy.zipPackage(options); + await rokuDeploy.zip({ + stagingDir: s`${tempDir}/path/to/nowhere`, + outDir: outDir + }); } catch (e) { err = (e as Error); } @@ -815,148 +767,6 @@ describe('RokuDeploy', () => { }); - describe('createPackage', () => { - it('works with custom stagingDir', async () => { - let opts = { - ...options, - files: [ - 'manifest' - ], - stagingDir: '.tmp/dist' - }; - await rokuDeploy.createPackage(opts); - expectPathExists(rokuDeploy.getOutputZipFilePath(opts)); - }); - - it('should throw error when no files were found to copy', async () => { - await assertThrowsAsync(async () => { - options.files = []; - await rokuDeploy.createPackage(options); - }); - }); - - it('should create package in proper directory', async () => { - await rokuDeploy.createPackage({ - ...options, - files: [ - 'manifest' - ] - }); - expectPathExists(rokuDeploy.getOutputZipFilePath(options)); - }); - - it('should only include the specified files', async () => { - const files = ['manifest']; - options.files = files; - await rokuDeploy.createPackage(options); - const data = fsExtra.readFileSync(rokuDeploy.getOutputZipFilePath(options)); - const zip = await JSZip.loadAsync(data as any); - - for (const file of files) { - const zipFileContents = await zip.file(file.toString()).async('string'); - const sourcePath = path.join(options.rootDir, file); - const incomingContents = fsExtra.readFileSync(sourcePath, 'utf8'); - expect(zipFileContents).to.equal(incomingContents); - } - }); - - it('generates full package with defaults', async () => { - const filePaths = writeFiles(rootDir, [ - 'components/components/Loader/Loader.brs', - 'images/splash_hd.jpg', - 'source/main.brs', - 'manifest' - ]); - await rokuDeploy.createPackage({ - ...options, - //target a subset of the files to make the test faster - files: filePaths - }); - - const data = fsExtra.readFileSync(rokuDeploy.getOutputZipFilePath(options)); - const zip = await JSZip.loadAsync(data as any); - - for (const file of filePaths) { - const zipFileContents = await zip.file(file.toString())?.async('string'); - const sourcePath = path.join(options.rootDir, file); - const incomingContents = fsExtra.readFileSync(sourcePath, 'utf8'); - expect(zipFileContents).to.equal(incomingContents); - } - }); - - it('should retain the staging directory when told to', async () => { - let stagingDirValue = await rokuDeploy.prepublishToStaging({ - ...options, - files: [ - 'manifest' - ] - }); - expectPathExists(stagingDirValue); - options.retainStagingDir = true; - await rokuDeploy.zipPackage(options); - expectPathExists(stagingDirValue); - }); - - it('should call our callback with correct information', async () => { - fsExtra.outputFileSync(`${rootDir}/manifest`, 'major_version=1'); - - let spy = sinon.spy((info: BeforeZipCallbackInfo) => { - expectPathExists(info.stagingDir); - expect(info.manifestData.major_version).to.equal('1'); - }); - - await rokuDeploy.createPackage(options, spy); - - if (spy.notCalled) { - assert.fail('Callback not called'); - } - }); - - it('should wait for promise returned by pre-zip callback', async () => { - fsExtra.outputFileSync(`${rootDir}/manifest`, ''); - let count = 0; - await rokuDeploy.createPackage({ - ...options, - files: ['manifest'] - }, (info) => { - return Promise.resolve().then(() => { - count++; - }).then(() => { - count++; - }); - }); - expect(count).to.equal(2); - }); - - it('should increment the build number if requested', async () => { - fsExtra.outputFileSync(`${rootDir}/manifest`, `build_version=0`); - options.incrementBuildNumber = true; - //make the zipping immediately resolve - sinon.stub(rokuDeploy, 'zipPackage').returns(Promise.resolve()); - let beforeZipInfo: BeforeZipCallbackInfo; - await rokuDeploy.createPackage({ - ...options, - files: ['manifest'] - }, (info) => { - beforeZipInfo = info; - }); - expect(beforeZipInfo.manifestData.build_version).to.not.equal('0'); - }); - - it('should not increment the build number if not requested', async () => { - fsExtra.outputFileSync(`${rootDir}/manifest`, `build_version=0`); - options.incrementBuildNumber = false; - await rokuDeploy.createPackage({ - ...options, - files: [ - 'manifest' - ] - }, (info) => { - expect(info.manifestData.build_version).to.equal('0'); - }); - }); - }); - it('runs via the command line using the rokudeploy.json file', function test() { this.timeout(20000); //build the project @@ -966,11 +776,11 @@ describe('RokuDeploy', () => { describe('generateBaseRequestOptions', () => { it('uses default port', () => { - expect(rokuDeploy['generateBaseRequestOptions']('a_b_c', { host: '1.2.3.4' }).url).to.equal('http://1.2.3.4:80/a_b_c'); + expect(rokuDeploy['generateBaseRequestOptions']('a_b_c', { host: '1.2.3.4', password: 'password' }).url).to.equal('http://1.2.3.4:80/a_b_c'); }); it('uses overridden port', () => { - expect(rokuDeploy['generateBaseRequestOptions']('a_b_c', { host: '1.2.3.4', packagePort: 999 }).url).to.equal('http://1.2.3.4:999/a_b_c'); + expect(rokuDeploy['generateBaseRequestOptions']('a_b_c', { host: '1.2.3.4', packagePort: 999, password: 'password' }).url).to.equal('http://1.2.3.4:999/a_b_c'); }); }); @@ -981,7 +791,7 @@ describe('RokuDeploy', () => { process.nextTick(callback, new Error()); return {} as any; }); - return rokuDeploy.pressHomeButton({}).then(() => { + return rokuDeploy.keyPress({ ...options, host: '1.2.3.4', key: 'home' }).then(() => { assert.fail('Should have rejected the promise'); }, () => { expect(true).to.be.true; @@ -991,34 +801,34 @@ describe('RokuDeploy', () => { it('uses default port', async () => { const promise = new Promise((resolve) => { sinon.stub(rokuDeploy, 'doPostRequest').callsFake((opts: any) => { - expect(opts.url).to.equal('http://1.2.3.4:8060/keypress/Home'); + expect(opts.url).to.equal('http://1.2.3.4:8060/keypress/home'); resolve(); }); }); - await rokuDeploy.pressHomeButton('1.2.3.4'); + await rokuDeploy.keyPress({ ...options, host: '1.2.3.4', key: 'home' }); await promise; }); it('uses overridden port', async () => { const promise = new Promise((resolve) => { sinon.stub(rokuDeploy, 'doPostRequest').callsFake((opts: any) => { - expect(opts.url).to.equal('http://1.2.3.4:987/keypress/Home'); + expect(opts.url).to.equal('http://1.2.3.4:987/keypress/home'); resolve(); }); }); - await rokuDeploy.pressHomeButton('1.2.3.4', 987); + await rokuDeploy.keyPress({ ...options, host: '1.2.3.4', remotePort: 987, key: 'home' }); await promise; }); it('uses default timeout', async () => { const promise = new Promise((resolve) => { sinon.stub(rokuDeploy, 'doPostRequest').callsFake((opts: any) => { - expect(opts.url).to.equal('http://1.2.3.4:8060/keypress/Home'); + expect(opts.url).to.equal('http://1.2.3.4:8060/keypress/home'); expect(opts.timeout).to.equal(150000); resolve(); }); }); - await rokuDeploy.pressHomeButton('1.2.3.4'); + await rokuDeploy.keyPress({ ...options, host: '1.2.3.4', key: 'home' }); await promise; }); @@ -1026,43 +836,45 @@ describe('RokuDeploy', () => { const promise = new Promise((resolve) => { sinon.stub(rokuDeploy, 'doPostRequest').callsFake((opts: any) => { - expect(opts.url).to.equal('http://1.2.3.4:987/keypress/Home'); + expect(opts.url).to.equal('http://1.2.3.4:987/keypress/home'); expect(opts.timeout).to.equal(1000); resolve(); }); }); - await rokuDeploy.pressHomeButton('1.2.3.4', 987, 1000); + await rokuDeploy.keyPress({ ...options, host: '1.2.3.4', remotePort: 987, key: 'home', timeout: 1000 }); await promise; }); }); let fileCounter = 1; - describe('publish', () => { + describe('sideload', () => { beforeEach(() => { - options.host = '0.0.0.0'; - //make a dummy output file...we don't care what's in it options.outFile = `temp${fileCounter++}.zip`; try { - fsExtra.outputFileSync(`${options.outDir}/${options.outFile}`, 'asdf'); + fsExtra.outputFileSync(`${outDir}/${options.outFile}`, 'asdf'); } catch (e) { } }); it('uses overridden route', async () => { const stub = mockDoPostRequest(); - await rokuDeploy.publish({ + await rokuDeploy.sideload({ ...options, + host: '0.0.0.0', + password: 'password', packageUploadOverrides: { route: 'alt_path' } }); - expect(stub.getCall(0).args[0].url).to.eql('http://0.0.0.0:80/alt_path'); + expect(stub.getCall(1).args[0].url).to.eql('http://0.0.0.0:80/alt_path'); }); it('overrides formData', async () => { const stub = mockDoPostRequest(); - await rokuDeploy.publish({ + await rokuDeploy.sideload({ ...options, + host: '1.2.3.4', + password: 'password', remoteDebug: true, packageUploadOverrides: { formData: { @@ -1071,41 +883,51 @@ describe('RokuDeploy', () => { } } }); - expect(stub.getCall(0).args[0].formData).to.include({ + expect(stub.getCall(1).args[0].formData).to.include({ newfield: 'here' }).and.to.not.haveOwnProperty('remotedebug'); }); it('does not delete the archive by default', async () => { - let zipPath = `${options.outDir}/${options.outFile}`; + let zipPath = `${outDir}/${options.outFile}`; mockDoPostRequest(); //the file should exist expect(fsExtra.pathExistsSync(zipPath)).to.be.true; - await rokuDeploy.publish(options); + await rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + outFile: options.outFile + }); //the file should still exist expect(fsExtra.pathExistsSync(zipPath)).to.be.true; }); it('deletes the archive when configured', async () => { - let zipPath = `${options.outDir}/${options.outFile}`; - + let zipPath = `${outDir}/${options.outFile}`; mockDoPostRequest(); //the file should exist expect(fsExtra.pathExistsSync(zipPath)).to.be.true; - await rokuDeploy.publish({ ...options, retainDeploymentArchive: false }); + await rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + retainDeploymentArchive: false, + outFile: options.outFile + }); //the file should not exist expect(fsExtra.pathExistsSync(zipPath)).to.be.false; //the out folder should also be deleted since it's empty }); it('failure to close read stream does not crash', async () => { - const orig = rokuDeploy.fsExtra.createReadStream; + const orig = fsExtra.createReadStream; //wrap the stream.close call so we can throw - sinon.stub(rokuDeploy.fsExtra, 'createReadStream').callsFake((pathLike) => { - const stream = orig.call(rokuDeploy.fsExtra, pathLike); + sinon.stub(fsExtra, 'createReadStream').callsFake((pathLike) => { + const stream = orig.call(fsExtra, pathLike); const originalClose = stream.close; stream.close = () => { originalClose.call(stream); @@ -1114,28 +936,47 @@ describe('RokuDeploy', () => { return stream; }); - let zipPath = `${options.outDir}/${options.outFile}`; + let zipPath = `${outDir}/${options.outFile}`; mockDoPostRequest(); //the file should exist expect(fsExtra.pathExistsSync(zipPath)).to.be.true; - await rokuDeploy.publish({ ...options, retainDeploymentArchive: false }); + await rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + retainDeploymentArchive: false, + outFile: options.outFile + }); //the file should not exist expect(fsExtra.pathExistsSync(zipPath)).to.be.false; //the out folder should also be deleted since it's empty }); it('fails when the zip file is missing', async () => { - options.outFile = 'fileThatDoesNotExist.zip'; await expectThrowsAsync(async () => { - await rokuDeploy.publish(options); - }, `Cannot publish because file does not exist at '${rokuDeploy.getOutputZipFilePath(options)}'`); + await rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + outFile: 'fileThatDoesNotExist.zip', + deleteDevChannel: false + }); + }, `Cannot sideload because file does not exist at '${rokuDeploy['getOutputZipFilePath']({ + outFile: 'fileThatDoesNotExist.zip', + outDir: outDir + })}'`); }); it('fails when no host is provided', () => { expectPathNotExists('rokudeploy.json'); - return rokuDeploy.publish({ host: undefined }).then(() => { + return rokuDeploy.sideload({ + host: undefined, + password: 'password', + outDir: outDir, + outFile: options.outFile + }).then(() => { assert.fail('Should not have succeeded'); }, () => { expect(true).to.be.true; @@ -1145,7 +986,7 @@ describe('RokuDeploy', () => { it('throws when package upload fails', async () => { //intercept the post requests sinon.stub(request, 'post').callsFake((data: any, callback: any) => { - if (data.url === `http://${options.host}/plugin_install`) { + if (data.url === `http://1.2.3.4/plugin_install`) { process.nextTick(() => { callback(new Error('Failed to publish to server')); }); @@ -1156,7 +997,12 @@ describe('RokuDeploy', () => { }); try { - await rokuDeploy.publish(options); + await rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + outFile: options.outFile + }); } catch (e) { assert.ok('Exception was thrown as expected'); return; @@ -1165,13 +1011,18 @@ describe('RokuDeploy', () => { }); it('rejects as CompileError when initial replace fails', () => { - options.failOnCompileError = true; mockDoPostRequest(` Install Failure: Compilation Failed. Shell.create('Roku.Message').trigger('Set message type', 'error').trigger('Set message content', 'Install Failure: Compilation Failed').trigger('Render', node); `); - return rokuDeploy.publish(options).then(() => { + return rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + failOnCompileError: true, + outFile: options.outFile + }).then(() => { assert.fail('Should not have succeeded due to roku server compilation failure'); }, (err) => { expect(err).to.be.instanceOf(errors.CompileError); @@ -1179,13 +1030,18 @@ describe('RokuDeploy', () => { }); it('rejects as CompileError when initial replace fails', () => { - options.failOnCompileError = true; mockDoPostRequest(` Install Failure: Compilation Failed. Shell.create('Roku.Message').trigger('Set message type', 'error').trigger('Set message content', 'Install Failure: Compilation Failed').trigger('Render', node); `); - return rokuDeploy.publish(options).then(() => { + return rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + failOnCompileError: true, + outFile: options.outFile + }).then(() => { assert.fail('Should not have succeeded due to roku server compilation failure'); }, (err) => { expect(err).to.be.instanceOf(errors.CompileError); @@ -1193,11 +1049,16 @@ describe('RokuDeploy', () => { }); it('rejects when response contains compile error wording', () => { - options.failOnCompileError = true; let body = 'Install Failure: Compilation Failed.'; mockDoPostRequest(body); - return rokuDeploy.publish(options).then(() => { + return rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + failOnCompileError: true, + outFile: options.outFile + }).then(() => { assert.fail('Should not have succeeded due to roku server compilation failure'); }, (err) => { expect(err.message).to.equal('Compile error'); @@ -1221,10 +1082,15 @@ describe('RokuDeploy', () => { }); it('rejects when response contains invalid password status code', () => { - options.failOnCompileError = true; mockDoPostRequest('', 401); - return rokuDeploy.publish(options).then(() => { + return rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + failOnCompileError: true, + outFile: options.outFile + }).then(() => { assert.fail('Should not have succeeded due to roku server compilation failure'); }, (err) => { expect(err.message).to.be.a('string').and.satisfy(msg => msg.startsWith('Unauthorized. Please verify credentials for host')); @@ -1237,7 +1103,14 @@ describe('RokuDeploy', () => { mockDoPostRequest(`'Failed to check for software update'`, 200); try { - await rokuDeploy.publish(options); + await rokuDeploy.sideload( + { + host: '1.2.3.4', + password: 'password', + outDir: outDir, + outFile: options.outFile + } + ); assert.fail('Should not have succeeded due to roku server compilation failure'); } catch (err) { expect((err as any).message).to.eql( @@ -1251,8 +1124,10 @@ describe('RokuDeploy', () => { let spy = sinon.stub(rokuDeploy as any, 'doPostRequest').callsFake((params: any) => { let results: any; if (params?.formData['mysubmit'] === 'Replace') { + // console.log('returning 500'); results = { response: { statusCode: 500 }, body: `'Failed to check for software update'` }; } else { + console.log('returning 200'); results = { response: { statusCode: 200 }, body: `` }; } rokuDeploy['checkRequest'](results); @@ -1260,7 +1135,15 @@ describe('RokuDeploy', () => { }); try { - await rokuDeploy.publish(options); + await rokuDeploy.sideload( + { + host: '1.2.3.4', + password: 'password', + outDir: outDir, + outFile: options.outFile, + deleteDevChannel: false + } + ); assert.fail('Should not have succeeded due to roku server compilation failure'); } catch (err) { expect(spy.callCount).to.eql(1); @@ -1277,14 +1160,22 @@ describe('RokuDeploy', () => { if (params?.formData['mysubmit'] === 'Replace') { results = { response: { statusCode: 500 }, body: `` }; } else { - results = { response: { statusCode: 500 }, body: `'Failed to check for software update'` }; + results = { response: { statusCode: 200 }, body: `'Failed to check for software update'` }; } rokuDeploy['checkRequest'](results); return Promise.resolve(results); }); try { - await rokuDeploy.publish(options); + await rokuDeploy.sideload( + { + host: '1.2.3.4', + password: 'password', + outDir: outDir, + outFile: options.outFile, + deleteDevChannel: false + } + ); assert.fail('Should not have succeeded due to roku server compilation failure'); } catch (err) { expect(spy.callCount).to.eql(2); @@ -1295,23 +1186,34 @@ describe('RokuDeploy', () => { }); it('handles successful deploy', () => { - options.failOnCompileError = true; mockDoPostRequest(); - return rokuDeploy.publish(options).then((result) => { - expect(result.message).to.equal('Successful deploy'); + return rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + failOnCompileError: true, + outFile: options.outFile + }).then((result) => { + expect(result.message).to.equal('Successful sideload'); }, () => { assert.fail('Should not have rejected the promise'); }); }); it('handles successful deploy with remoteDebug', () => { - options.failOnCompileError = true; - options.remoteDebug = true; const stub = mockDoPostRequest(); - return rokuDeploy.publish(options).then((result) => { - expect(result.message).to.equal('Successful deploy'); + return rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + failOnCompileError: true, + remoteDebug: true, + outFile: options.outFile, + deleteDevChannel: false + }).then((result) => { + expect(result.message).to.equal('Successful sideload'); expect(stub.getCall(0).args[0].formData.remotedebug).to.eql('1'); }, () => { assert.fail('Should not have rejected the promise'); @@ -1319,13 +1221,19 @@ describe('RokuDeploy', () => { }); it('handles successful deploy with remotedebug_connect_early', () => { - options.failOnCompileError = true; - options.remoteDebug = true; - options.remoteDebugConnectEarly = true; const stub = mockDoPostRequest(); - return rokuDeploy.publish(options).then((result) => { - expect(result.message).to.equal('Successful deploy'); + return rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + failOnCompileError: true, + remoteDebug: true, + remoteDebugConnectEarly: true, + outFile: options.outFile, + deleteDevChannel: false + }).then((result) => { + expect(result.message).to.equal('Successful sideload'); expect(stub.getCall(0).args[0].formData.remotedebug_connect_early).to.eql('1'); }, () => { assert.fail('Should not have rejected the promise'); @@ -1336,45 +1244,150 @@ describe('RokuDeploy', () => { delete options.appType; const stub = mockDoPostRequest(); - const result = await rokuDeploy.publish(options); - expect(result.message).to.equal('Successful deploy'); + fsExtra.outputFileSync(`${outDir}/${options.outFile}`, 'asdf'); + + const result = await rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + outFile: options.outFile, + deleteDevChannel: false + }); + expect(result.message).to.equal('Successful sideload'); expect(stub.getCall(0).args[0].formData.app_type).to.be.undefined; }); it('does not set appType if not appType is set to null or undefined', async () => { - options.appType = null; const stub = mockDoPostRequest(); + fsExtra.outputFileSync(`${outDir}/${options.outFile}`, 'asdf'); - const result = await rokuDeploy.publish(options); - expect(result.message).to.equal('Successful deploy'); + const result = await rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + outFile: options.outFile, + deleteDevChannel: false, + appType: null + }); + expect(result.message).to.equal('Successful sideload'); expect(stub.getCall(0).args[0].formData.app_type).to.be.undefined; }); it('sets appType="channel" when defined', async () => { - options.appType = 'channel'; const stub = mockDoPostRequest(); + fsExtra.outputFileSync(`${outDir}/${options.outFile}`, 'asdf'); - const result = await rokuDeploy.publish(options); - expect(result.message).to.equal('Successful deploy'); + const result = await rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + outFile: options.outFile, + deleteDevChannel: false, + appType: 'channel' + }); + expect(result.message).to.equal('Successful sideload'); expect(stub.getCall(0).args[0].formData.app_type).to.eql('channel'); }); - it('sets appType="channel" when defined', async () => { - options.appType = 'dcl'; + it('sets appType="dcl" when defined', async () => { const stub = mockDoPostRequest(); + fsExtra.outputFileSync(`${outDir}/${options.outFile}`, 'asdf'); - const result = await rokuDeploy.publish(options); - expect(result.message).to.equal('Successful deploy'); + const result = await rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + outFile: options.outFile, + deleteDevChannel: false, + appType: 'dcl' + }); + expect(result.message).to.equal('Successful sideload'); expect(stub.getCall(0).args[0].formData.app_type).to.eql('dcl'); }); - it('Does not reject when response contains compile error wording but config is set to ignore compile warnings', () => { + it('Does not reject when response contains compile error wording but config is set to ignore compile warnings', async () => { + const stub = mockDoPostRequest(); options.failOnCompileError = false; + const result = await rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + failOnCompileError: true, + remoteDebug: true, + remoteDebugConnectEarly: true, + outFile: options.outFile, + deleteDevChannel: false + }); + expect(result.message).to.equal('Successful sideload'); + expect(stub.getCall(0).args[0].formData.app_type).to.be.undefined; + }); + + it('does not set appType if not appType is set to null or undefined', async () => { + const stub = mockDoPostRequest(); + + const result = await rokuDeploy.sideload({ + appType: null, + host: '1.2.3.4', + password: 'password', + outDir: outDir, + failOnCompileError: true, + remoteDebug: true, + remoteDebugConnectEarly: true, + outFile: options.outFile, + deleteDevChannel: false + }); + expect(result.message).to.equal('Successful sideload'); + expect(stub.getCall(0).args[0].formData.app_type).to.be.undefined; + }); + + it('sets appType="channel" when defined', async () => { + const stub = mockDoPostRequest(); + + const result = await rokuDeploy.sideload({ + appType: 'channel', + host: '1.2.3.4', + password: 'password', + outDir: outDir, + failOnCompileError: true, + remoteDebug: true, + remoteDebugConnectEarly: true, + outFile: options.outFile, + deleteDevChannel: false + }); + expect(result.message).to.equal('Successful sideload'); + expect(stub.getCall(0).args[0].formData.app_type).to.eql('channel'); + }); + + it('sets appType="channel" when defined', async () => { + const stub = mockDoPostRequest(); + + const result = await rokuDeploy.sideload({ + appType: 'dcl', + host: '1.2.3.4', + password: 'password', + outDir: outDir, + failOnCompileError: true, + remoteDebug: true, + remoteDebugConnectEarly: true, + outFile: options.outFile, + deleteDevChannel: false + }); + expect(result.message).to.equal('Successful sideload'); + expect(stub.getCall(0).args[0].formData.app_type).to.eql('dcl'); + }); + + it('Does not reject when response contains compile error wording but config is set to ignore compile warnings', () => { let body = 'Identical to previous version -- not replacing.'; mockDoPostRequest(body); - return rokuDeploy.publish(options).then((result) => { + return rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + failOnCompileError: false, + outFile: options.outFile + }).then((result) => { expect(result.results.body).to.equal(body); }, () => { assert.fail('Should have resolved promise'); @@ -1382,12 +1395,17 @@ describe('RokuDeploy', () => { }); it('rejects when response is unknown status code', async () => { - options.failOnCompileError = true; let body = 'Identical to previous version -- not replacing.'; mockDoPostRequest(body, 123); try { - await rokuDeploy.publish(options); + await rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + failOnCompileError: true, + outFile: options.outFile + }); } catch (e) { expect(e).to.be.instanceof(errors.InvalidDeviceResponseCodeError); return; @@ -1396,11 +1414,16 @@ describe('RokuDeploy', () => { }); it('rejects when user is unauthorized', async () => { - options.failOnCompileError = true; mockDoPostRequest('', 401); try { - await rokuDeploy.publish(options); + await rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + failOnCompileError: true, + outFile: options.outFile + }); } catch (e) { expect(e).to.be.instanceof(errors.UnauthorizedDeviceResponseError); return; @@ -1409,11 +1432,16 @@ describe('RokuDeploy', () => { }); it('rejects when encountering an undefined response', async () => { - options.failOnCompileError = true; mockDoPostRequest(null); try { - await rokuDeploy.publish(options); + await rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + failOnCompileError: true, + outFile: options.outFile + }); } catch (e) { assert.ok('Exception was thrown as expected'); return; @@ -1426,7 +1454,13 @@ describe('RokuDeploy', () => { let spy = mockDoPostRequest('', 577); try { - await rokuDeploy.publish(options); + await rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + outFile: options.outFile, + deleteDevChannel: false + }); } catch (e) { expect(spy.callCount).to.eql(1); assert.ok('Exception was thrown as expected'); @@ -1450,7 +1484,13 @@ describe('RokuDeploy', () => { }); try { - await rokuDeploy.publish(options); + await rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + outFile: options.outFile, + deleteDevChannel: false + }); } catch (e) { expect(spy.callCount).to.eql(2); assert.ok('Exception was thrown as expected'); @@ -1469,14 +1509,19 @@ describe('RokuDeploy', () => { } } - it('Should throw an excpetion', async () => { + it('Should throw an exception', async () => { options.failOnCompileError = true; sinon.stub(rokuDeploy as any, 'doPostRequest').callsFake((params) => { throw new ErrorWithConnectionResetCode(); }); try { - await rokuDeploy.publish(options); + await rokuDeploy.sideload({ + host: '1.2.3.4', + password: 'password', + outDir: outDir, + outFile: options.outFile + }); } catch (e) { assert.ok('Exception was thrown as expected'); expect(e).to.be.instanceof(errors.ConnectionResetError); @@ -1489,25 +1534,19 @@ describe('RokuDeploy', () => { describe('convertToSquashfs', () => { it('should not return an error if successful', async () => { mockDoPostRequest('Conversion succeeded


Parallel mksquashfs: Using 1 processor'); - await rokuDeploy.convertToSquashfs(options); - }); - - it('should return MissingRequiredOptionError if host was not provided', async () => { - mockDoPostRequest(); - try { - options.host = undefined; - await rokuDeploy.convertToSquashfs(options); - } catch (e) { - expect(e).to.be.instanceof(errors.MissingRequiredOptionError); - return; - } - assert.fail('Should not have succeeded'); + await rokuDeploy.convertToSquashfs({ + host: options.host, + password: 'password' + }); }); it('should return ConvertError if converting failed', async () => { mockDoPostRequest(); try { - await rokuDeploy.convertToSquashfs(options); + await rokuDeploy.convertToSquashfs({ + host: options.host, + password: 'password' + }); } catch (e) { expect(e).to.be.instanceof(errors.ConvertError); return; @@ -1522,7 +1561,11 @@ describe('RokuDeploy', () => { }); doPostStub.onSecondCall().returns({ body: '..."fileType":"squashfs"...' }); try { - await rokuDeploy.convertToSquashfs(options); + await rokuDeploy.convertToSquashfs({ + ...options, + host: options.host, + password: 'password' + }); } catch (e) { assert.fail('Should not have throw'); } @@ -1534,7 +1577,11 @@ describe('RokuDeploy', () => { throw new ErrorWithCode('Something else'); }); try { - await rokuDeploy.convertToSquashfs(options); + await rokuDeploy.convertToSquashfs({ + ...options, + host: options.host, + password: 'password' + }); } catch (e) { expect(e).to.be.instanceof(ErrorWithCode); expect(e['code']).to.be.eql('Something else'); @@ -1550,7 +1597,11 @@ describe('RokuDeploy', () => { }); doPostStub.onSecondCall().returns({ body: '..."fileType":"zip"...' }); try { - await rokuDeploy.convertToSquashfs(options); + await rokuDeploy.convertToSquashfs({ + ...options, + host: options.host, + password: 'password' + }); } catch (e) { expect(e).to.be.instanceof(errors.ConvertError); return; @@ -1567,7 +1618,11 @@ describe('RokuDeploy', () => { throw new Error('Never seen'); }); try { - await rokuDeploy.convertToSquashfs(options); + await rokuDeploy.convertToSquashfs({ + ...options, + host: options.host, + password: 'password' + }); } catch (e) { expect(e).to.be.instanceof(ErrorWithCode); return; @@ -1599,7 +1654,13 @@ describe('RokuDeploy', () => { sinon.stub(fsExtra, 'createReadStream').throws(expectedError); let actualError: Error; try { - await rokuDeploy.rekeyDevice(options); + await rokuDeploy.rekeyDevice({ + host: '1.2.3.4', + password: 'password', + rekeySignedPackage: options.rekeySignedPackage, + signingPassword: options.signingPassword, + devId: options.devId + }); } catch (e) { actualError = e as Error; } @@ -1611,11 +1672,19 @@ describe('RokuDeploy', () => { Success. `; mockDoPostRequest(body); - options.rekeySignedPackage = s`../notReal.pkg`; - fsExtra.outputFileSync(s`${tempDir}/notReal.pkg`, ''); - //small sleep to ensure the file exists (hack for testing!) - await util.sleep(10); - await rokuDeploy.rekeyDevice(options); + + try { + fsExtra.writeFileSync(s`notReal.pkg`, ''); + await rokuDeploy.rekeyDevice({ + host: '1.2.3.4', + password: 'password', + rekeySignedPackage: s`notReal.pkg`, + signingPassword: options.signingPassword, + devId: options.devId + }); + } finally { + fsExtra.removeSync(s`notReal.pkg`); + } }); it('should work with absolute path', async () => { @@ -1623,9 +1692,13 @@ describe('RokuDeploy', () => { Success. `; mockDoPostRequest(body); - - options.rekeySignedPackage = s`${tempDir}/testSignedPackage.pkg`; - await rokuDeploy.rekeyDevice(options); + await rokuDeploy.rekeyDevice({ + host: '1.2.3.4', + password: 'password', + rekeySignedPackage: s`${tempDir}/testSignedPackage.pkg`, + signingPassword: options.signingPassword, + devId: options.devId + }); }); it('should not return an error if dev ID is set and matches output', async () => { @@ -1633,7 +1706,13 @@ describe('RokuDeploy', () => { Success. `; mockDoPostRequest(body); - await rokuDeploy.rekeyDevice(options); + await rokuDeploy.rekeyDevice({ + host: '1.2.3.4', + password: 'password', + rekeySignedPackage: options.rekeySignedPackage, + signingPassword: options.signingPassword, + devId: options.devId + }); }); it('should not return an error if dev ID is not set', async () => { @@ -1641,36 +1720,25 @@ describe('RokuDeploy', () => { Success. `; mockDoPostRequest(body); - options.devId = undefined; - await rokuDeploy.rekeyDevice(options); - }); - - it('should throw error if missing rekeySignedPackage option', async () => { - try { - options.rekeySignedPackage = null; - await rokuDeploy.rekeyDevice(options); - } catch (e) { - expect(e).to.be.instanceof(errors.MissingRequiredOptionError); - return; - } - assert.fail('Exception should have been thrown'); - }); - - it('should throw error if missing signingPassword option', async () => { - try { - options.signingPassword = null; - await rokuDeploy.rekeyDevice(options); - } catch (e) { - expect(e).to.be.instanceof(errors.MissingRequiredOptionError); - return; - } - assert.fail('Exception should have been thrown'); + await rokuDeploy.rekeyDevice({ + host: '1.2.3.4', + password: 'password', + rekeySignedPackage: options.rekeySignedPackage, + signingPassword: options.signingPassword, + devId: undefined + }); }); it('should throw error if response is not parsable', async () => { try { mockDoPostRequest(); - await rokuDeploy.rekeyDevice(options); + await rokuDeploy.rekeyDevice({ + host: '1.2.3.4', + password: 'password', + rekeySignedPackage: options.rekeySignedPackage, + signingPassword: options.signingPassword, + devId: options.devId + }); } catch (e) { expect(e).to.be.instanceof(errors.UnparsableDeviceResponseError); return; @@ -1684,7 +1752,13 @@ describe('RokuDeploy', () => { Invalid public key. `; mockDoPostRequest(body); - await rokuDeploy.rekeyDevice(options); + await rokuDeploy.rekeyDevice({ + host: '1.2.3.4', + password: 'password', + rekeySignedPackage: options.rekeySignedPackage, + signingPassword: options.signingPassword, + devId: options.devId + }); } catch (e) { expect(e).to.be.instanceof(errors.FailedDeviceResponseError); return; @@ -1698,9 +1772,13 @@ describe('RokuDeploy', () => { Success. `; mockDoPostRequest(body); - - options.devId = '45fdc2019903ac333ff624b0b2cddd2c733c3e74'; - await rokuDeploy.rekeyDevice(options); + await rokuDeploy.rekeyDevice({ + host: '1.2.3.4', + password: 'password', + rekeySignedPackage: options.rekeySignedPackage, + signingPassword: options.signingPassword, + devId: '45fdc2019903ac333ff624b0b2cddd2c733c3e74' + }); } catch (e) { expect(e).to.be.instanceof(errors.UnknownDeviceResponseError); return; @@ -1709,17 +1787,36 @@ describe('RokuDeploy', () => { }); }); - describe('signExistingPackage', () => { + describe('createSignedPackage', () => { + let onHandler: any; beforeEach(() => { - fsExtra.outputFileSync(`${stagingDir}/manifest`, ``); - }); + fsExtra.outputFileSync(`${tempDir}/manifest`, ` + title=RokuDeployTestChannel + major_version=1 + minor_version=0`); + sinon.stub(fsExtra, 'ensureDir').callsFake(((pth: string, callback: (err: Error) => void) => { + //do nothing, assume the dir gets created + }) as any); - it('should return our error if signingPassword is not supplied', async () => { - options.signingPassword = undefined; - await expectThrowsAsync(async () => { - await rokuDeploy.signExistingPackage(options); - }, 'Must supply signingPassword'); - }); + //intercept the http request + sinon.stub(request, 'get').callsFake(() => { + let req: any = { + on: (event, callback) => { + process.nextTick(() => { + onHandler(event, callback); + }); + return req; + }, + pipe: async () => { + //if a write stream gets created, write some stuff and close it + const writeStream = await writeStreamPromise; + writeStream.write('test'); + writeStream.close(); + } + }; + return req; + }); + }); it('should return an error if there is a problem with the network request', async () => { let error = new Error('Network Error'); @@ -1729,7 +1826,12 @@ describe('RokuDeploy', () => { process.nextTick(callback, error); return {} as any; }); - await rokuDeploy.signExistingPackage(options); + await rokuDeploy.createSignedPackage({ + host: '1.2.3.4', + password: 'password', + signingPassword: options.signingPassword, + manifestPath: s`${tempDir}/manifest` + }); } catch (e) { expect(e).to.equal(error); return; @@ -1740,7 +1842,12 @@ describe('RokuDeploy', () => { it('should return our error if it received invalid data', async () => { try { mockDoPostRequest(null); - await rokuDeploy.signExistingPackage(options); + await rokuDeploy.createSignedPackage({ + host: '1.2.3.4', + password: 'password', + signingPassword: options.signingPassword, + manifestPath: s`${tempDir}/manifest` + }); } catch (e) { expect(e).to.be.instanceof(errors.UnparsableDeviceResponseError); return; @@ -1756,7 +1863,12 @@ describe('RokuDeploy', () => { mockDoPostRequest(body); await expectThrowsAsync( - rokuDeploy.signExistingPackage(options), + rokuDeploy.createSignedPackage({ + host: '1.2.3.4', + password: 'password', + signingPassword: options.signingPassword, + manifestPath: s`${tempDir}/manifest` + }), 'Invalid Password.' ); }); @@ -1767,41 +1879,176 @@ describe('RokuDeploy', () => { node.appendChild(pkgDiv);`; mockDoPostRequest(body); - let pkgPath = await rokuDeploy.signExistingPackage(options); - expect(pkgPath).to.equal('pkgs//P6953175d5df120c0069c53de12515b9a.pkg'); + const stub = sinon.stub(rokuDeploy as any, 'downloadFile').returns(Promise.resolve('pkgs//P6953175d5df120c0069c53de12515b9a.pkg')); + + let pkgPath = await rokuDeploy.createSignedPackage({ + host: '1.2.3.4', + password: 'password', + signingPassword: options.signingPassword, + outDir: outDir, + manifestPath: s`${tempDir}/manifest` + }); + expect(pkgPath).to.equal(s`${outDir}/roku-deploy.pkg`); + expect(stub.getCall(0).args[0].url).to.equal('http://1.2.3.4:80/pkgs//P6953175d5df120c0069c53de12515b9a.pkg'); }); it('should return created pkg from SD card on success', async () => { mockDoPostRequest(fakePluginPackageResponse); - let pkgPath = await rokuDeploy.signExistingPackage(options); + let pkgPath = await rokuDeploy.createSignedPackage({ + host: '1.2.3.4', + password: 'password', + signingPassword: options.signingPassword, + manifestPath: s`${tempDir}/manifest` + }); expect(pkgPath).to.equal('pkgs/sdcard0/Pae6cec1eab06a45ca1a7f5b69edd3a20.pkg'); }); it('should return our fallback error if neither error or package link was detected', async () => { mockDoPostRequest(); await expectThrowsAsync( - rokuDeploy.signExistingPackage(options), + rokuDeploy.createSignedPackage({ + host: '1.2.3.4', + password: 'password', + signingPassword: options.signingPassword, + manifestPath: s`${tempDir}/manifest` + }), 'Unknown error signing package' ); }); + + it('should return error if dev id does not match', async () => { + mockDoGetRequest(` + + 789 + + `); + await expectThrowsAsync( + rokuDeploy.createSignedPackage({ + host: '1.2.3.4', + password: 'password', + signingPassword: options.signingPassword, + devId: '123', + manifestPath: s`${tempDir}/manifest` + }), + `Package signing cancelled: provided devId '123' does not match on-device devId '789'` + ); + }); + + it('should return error if neither manifestPath nor appTitle and appVersion are provided', async () => { + await expectThrowsAsync( + rokuDeploy.createSignedPackage({ + host: '1.2.3.4', + password: 'password', + signingPassword: options.signingPassword, + devId: '123' + }), + `Either appTitle and appVersion or manifestPath must be provided` + ); + }); + + it('should return error if major or minor version is missing from manifest', async () => { + fsExtra.outputFileSync(`${tempDir}/manifest`, `title=AwesomeApp`); + await expectThrowsAsync( + rokuDeploy.createSignedPackage({ + host: '1.2.3.4', + password: 'password', + signingPassword: options.signingPassword, + devId: '123', + manifestPath: s`${tempDir}/manifest` + }), + `Either major or minor version is missing from the manifest` + ); + }); + + it('should return error if value for appTitle is missing from manifest', async () => { + fsExtra.outputFileSync(`${tempDir}/manifest`, `major_version=1\nminor_version=0`); + await expectThrowsAsync( + rokuDeploy.createSignedPackage({ + host: '1.2.3.4', + password: 'password', + signingPassword: options.signingPassword, + devId: '123', + manifestPath: s`${tempDir}/manifest` + }), + `Value for appTitle is missing from the manifest` + ); + }); + + it('returns a pkg file path on success', async () => { + //the write stream should return null, which causes a specific branch to be executed + createWriteStreamStub.callsFake(() => { + return null; + }); + + // let onHandler: any; + onHandler = (event, callback) => { + if (event === 'response') { + callback({ + statusCode: 200 + }); + } + }; + + let body = `var pkgDiv = document.createElement('div'); + pkgDiv.innerHTML = '
P6953175d5df120c0069c53de12515b9a.pkg
package file (7360 bytes)
'; + node.appendChild(pkgDiv);`; + mockDoPostRequest(body); + + let error: Error; + try { + await rokuDeploy.createSignedPackage({ + host: '1.2.3.4', + password: 'password', + signingPassword: options.signingPassword, + manifestPath: s`${tempDir}/manifest` + }); + } catch (e) { + error = e as any; + } + expect(error.message.startsWith('Unable to create write stream for')).to.be.true; + }); + + it('throws when error in request is encountered', async () => { + onHandler = (event, callback) => { + if (event === 'error') { + callback(new Error('Some error')); + } + }; + + let body = `var pkgDiv = document.createElement('div'); + pkgDiv.innerHTML = '
P6953175d5df120c0069c53de12515b9a.pkg
package file (7360 bytes)
'; + node.appendChild(pkgDiv);`; + mockDoPostRequest(body); + + await expectThrowsAsync( + rokuDeploy.createSignedPackage({ + host: '1.2.3.4', + password: 'aaaa', + signingPassword: options.signingPassword, + manifestPath: s`${tempDir}/manifest` + }), + 'Some error' + ); + }); }); - describe('prepublishToStaging', () => { + describe('stage', () => { it('should use outDir for staging folder', async () => { - await rokuDeploy.prepublishToStaging({ + await rokuDeploy.stage({ files: [ 'manifest' - ] + ], + rootDir: rootDir }); expectPathExists(`${stagingDir}`); }); it('should support overriding the staging folder', async () => { - await rokuDeploy.prepublishToStaging({ - ...options, + await rokuDeploy.stage({ files: ['manifest'], - stagingDir: `${tempDir}/custom-out-dir` + stagingDir: `${tempDir}/custom-out-dir`, + rootDir: rootDir }); expectPathExists(`${tempDir}/custom-out-dir`); }); @@ -1811,11 +2058,14 @@ describe('RokuDeploy', () => { 'manifest', 'source/main.brs' ]); - options.files = [ - 'manifest', - 'source/main.brs' - ]; - await rokuDeploy.prepublishToStaging(options); + await rokuDeploy.stage({ + files: [ + 'manifest', + 'source/main.brs' + ], + rootDir: rootDir, + stagingDir: stagingDir + }); expectPathExists(`${stagingDir}/manifest`); expectPathExists(`${stagingDir}/source/main.brs`); }); @@ -1825,14 +2075,17 @@ describe('RokuDeploy', () => { 'manifest', 'source/main.brs' ]); - options.files = [ - 'manifest', - { - src: 'source/**/*', - dest: 'source' - } - ]; - await rokuDeploy.prepublishToStaging(options); + await rokuDeploy.stage({ + files: [ + 'manifest', + { + src: 'source/**/*', + dest: 'source' + } + ], + rootDir: rootDir, + stagingDir: stagingDir + }); expectPathExists(`${stagingDir}/source/main.brs`); }); @@ -1841,21 +2094,24 @@ describe('RokuDeploy', () => { 'manifest', 'source/main.brs' ]); - options.files = [ - { - src: 'manifest', - dest: '' - }, - { - src: 'source/**/*', - dest: 'source/' - }, - { - src: 'source/main.brs', - dest: 'source/main.brs' - } - ]; - await rokuDeploy.prepublishToStaging(options); + await rokuDeploy.stage({ + files: [ + { + src: 'manifest', + dest: '' + }, + { + src: 'source/**/*', + dest: 'source/' + }, + { + src: 'source/main.brs', + dest: 'source/main.brs' + } + ], + rootDir: rootDir, + stagingDir: stagingDir + }); expectPathExists(`${stagingDir}/manifest`); expectPathExists(`${stagingDir}/source/main.brs`); }); @@ -1865,17 +2121,20 @@ describe('RokuDeploy', () => { 'manifest', 'source/main.brs' ]); - options.files = [ - { - src: 'manifest', - dest: '' - }, - { - src: 'source/main.brs', - dest: 'source/renamed.brs' - } - ]; - await rokuDeploy.prepublishToStaging(options); + await rokuDeploy.stage({ + files: [ + { + src: 'manifest', + dest: '' + }, + { + src: 'source/main.brs', + dest: 'source/renamed.brs' + } + ], + rootDir: rootDir, + stagingDir: stagingDir + }); expectPathExists(`${stagingDir}/source/renamed.brs`); }); @@ -1883,17 +2142,20 @@ describe('RokuDeploy', () => { writeFiles(rootDir, [ 'manifest' ]); - options.files = [ - { - src: `${rootDir}/manifest`, - dest: '' - }, - { - src: 'source/main.brs', - dest: 'source/renamed.brs' - } - ]; - await rokuDeploy.prepublishToStaging(options); + await rokuDeploy.stage({ + files: [ + { + src: sp`${rootDir}/manifest`, + dest: '' + }, + { + src: 'source/main.brs', + dest: 'source/renamed.brs' + } + ], + rootDir: rootDir, + stagingDir: stagingDir + }); expectPathExists(`${stagingDir}/manifest`); }); @@ -1903,13 +2165,15 @@ describe('RokuDeploy', () => { 'components/loader/loader.brs', 'components/scenes/home/home.brs' ]); - options.files = [ - 'manifest', - 'components/!(scenes)/**/*' - ]; - options.retainStagingFolder = true; console.log('before'); - await rokuDeploy.prepublishToStaging(options); + await rokuDeploy.stage({ + files: [ + 'manifest', + 'components/!(scenes)/**/*' + ], + rootDir: rootDir, + stagingDir: stagingDir + }); console.log('after'); expectPathExists(s`${stagingDir}/components/loader/loader.brs`); expectPathNotExists(s`${stagingDir}/components/scenes/home/home.brs`); @@ -1921,27 +2185,30 @@ describe('RokuDeploy', () => { 'components/Loader/Loader.brs', 'components/scenes/Home/Home.brs' ]); - options.retainStagingFolder = true; - await rokuDeploy.prepublishToStaging({ - ...options, files: [ + await rokuDeploy.stage({ + files: [ 'manifest', 'source', 'components/**/*', '!components/scenes/**/*' - ] + ], + rootDir: rootDir, + stagingDir: stagingDir }); expectPathExists(`${stagingDir}/components/Loader/Loader.brs`); expectPathNotExists(`${stagingDir}/components/scenes/Home/Home.brs`); }); it('throws on invalid entries', async () => { - options.files = [ - 'manifest', - {} - ]; - options.retainStagingFolder = true; try { - await rokuDeploy.prepublishToStaging(options); + await rokuDeploy.stage({ + files: [ + 'manifest', + {} + ], + rootDir: rootDir, + stagingDir: stagingDir + }); expect(true).to.be.false; } catch (e) { expect(true).to.be.true; @@ -1950,14 +2217,17 @@ describe('RokuDeploy', () => { it('retains subfolder structure when referencing a folder', async () => { fsExtra.outputFileSync(`${rootDir}/flavors/shared/resources/images/fhd/image.jpg`, ''); - options.files = [ - 'manifest', - { - src: 'flavors/shared/resources/**/*', - dest: 'resources' - } - ]; - await rokuDeploy.prepublishToStaging(options); + await rokuDeploy.stage({ + files: [ + 'manifest', + { + src: 'flavors/shared/resources/**/*', + dest: 'resources' + } + ], + rootDir: rootDir, + stagingDir: stagingDir + }); expectPathExists(`${stagingDir}/resources/images/fhd/image.jpg`); }); @@ -1967,15 +2237,18 @@ describe('RokuDeploy', () => { 'flavors/shared/resources/images/fhd/image.jpg', 'resources/image.jpg' ]); - options.files = [ - 'manifest', - { - //the relative structure after /resources should be retained - src: 'flavors/shared/resources/**/*', - dest: 'resources' - } - ]; - await rokuDeploy.prepublishToStaging(options); + await rokuDeploy.stage({ + files: [ + 'manifest', + { + //the relative structure after /resources should be retained + src: 'flavors/shared/resources/**/*', + dest: 'resources' + } + ], + rootDir: rootDir, + stagingDir: stagingDir + }); expectPathExists(s`${stagingDir}/resources/images/fhd/image.jpg`); expectPathNotExists(s`${stagingDir}/resources/image.jpg`); }); @@ -2066,7 +2339,14 @@ describe('RokuDeploy', () => { dest: s`renamed_test.md` }]); - await rokuDeploy.prepublishToStaging(opts); + await rokuDeploy.stage({ + rootDir: rootDir, + stagingDir: stagingDir, + files: [ + 'manifest', + 'renamed_test.md' + ] + }); let stagedFilePath = s`${stagingDirValue}/renamed_test.md`; expectPathExists(stagedFilePath); let fileContents = await fsExtra.readFile(stagedFilePath); @@ -2111,44 +2391,117 @@ describe('RokuDeploy', () => { dest: s`source/main.brs` }]); - await rokuDeploy.prepublishToStaging(opts); + await rokuDeploy.stage({ + files: [ + 'manifest', + 'source/**/*' + ], + rootDir: s`${tempDir}/mainProject` + }); expect(fsExtra.pathExistsSync(`${stagingPath}/source/lib/promise/promise.brs`)); }); }); - }); + it('is resilient to file system errors', async () => { + let copy = fsExtra.copy; + let count = 0; - describe('normalizeFilesArray', () => { - it('catches invalid dest entries', () => { - expect(() => { - rokuDeploy['normalizeFilesArray']([{ - src: 'some/path', - dest: true - }]); - }).to.throw(); + //mock writeFile so we can throw a few errors during the test + sinon.stub(fsExtra, 'copy').callsFake((...args) => { + count += 1; + //fail a few times + if (count < 5) { + throw new Error('fake error thrown as part of the unit test'); + } else { + return copy.apply(fsExtra, args); + } + }); - expect(() => { - rokuDeploy['normalizeFilesArray']([{ - src: 'some/path', - dest: false - }]); - }).to.throw(); + //override the retry milliseconds to make test run faster + let orig = util.tryRepeatAsync.bind(util); + sinon.stub(util, 'tryRepeatAsync').callsFake(async (...args) => { + return orig(args[0], args[1], 0); + }); - expect(() => { - rokuDeploy['normalizeFilesArray']([{ - src: 'some/path', - dest: /asdf/gi - }]); - }).to.throw(); + fsExtra.outputFileSync(`${rootDir}/source/main.brs`, ''); - expect(() => { - rokuDeploy['normalizeFilesArray']([{ - src: 'some/path', - dest: {} - }]); - }).to.throw(); + await rokuDeploy.stage({ + rootDir: rootDir, + stagingDir: stagingDir, + files: [ + 'source/main.brs' + ] + }); + expectPathExists(s`${stagingDir}/source/main.brs`); + expect(count).to.be.greaterThan(4); + }); - expect(() => { - rokuDeploy['normalizeFilesArray']([{ + it('throws underlying error after the max fs error threshold is reached', async () => { + let copy = fsExtra.copy; + let count = 0; + + //mock writeFile so we can throw a few errors during the test + sinon.stub(fsExtra, 'copy').callsFake((...args) => { + count += 1; + //fail a few times + if (count < 15) { + throw new Error('fake error thrown as part of the unit test'); + } else { + return copy.apply(fsExtra, args); + } + }); + + //override the timeout for tryRepeatAsync so this test runs faster + let orig = util.tryRepeatAsync.bind(util); + sinon.stub(util, 'tryRepeatAsync').callsFake(async (...args) => { + return orig(args[0], args[1], 0); + }); + + fsExtra.outputFileSync(`${rootDir}/source/main.brs`, ''); + await expectThrowsAsync( + rokuDeploy.stage({ + rootDir: rootDir, + stagingDir: stagingDir, + files: [ + 'source/main.brs' + ] + }), + 'fake error thrown as part of the unit test' + ); + }); + }); + + describe('normalizeFilesArray', () => { + it('catches invalid dest entries', () => { + expect(() => { + util['normalizeFilesArray']([{ + src: 'some/path', + dest: true + }]); + }).to.throw(); + + expect(() => { + util['normalizeFilesArray']([{ + src: 'some/path', + dest: false + }]); + }).to.throw(); + + expect(() => { + util['normalizeFilesArray']([{ + src: 'some/path', + dest: /asdf/gi + }]); + }).to.throw(); + + expect(() => { + util['normalizeFilesArray']([{ + src: 'some/path', + dest: {} + }]); + }).to.throw(); + + expect(() => { + util['normalizeFilesArray']([{ src: 'some/path', dest: [] }]); @@ -2156,17 +2509,17 @@ describe('RokuDeploy', () => { }); it('normalizes directory separators paths', () => { - expect(rokuDeploy['normalizeFilesArray']([{ + expect(util['normalizeFilesArray']([{ src: `long/source/path`, dest: `long/dest/path` }])).to.eql([{ - src: s`long/source/path`, + src: sp`long/source/path`, dest: s`long/dest/path` }]); }); it('works for simple strings', () => { - expect(rokuDeploy['normalizeFilesArray']([ + expect(util['normalizeFilesArray']([ 'manifest', 'source/main.brs' ])).to.eql([ @@ -2176,7 +2529,7 @@ describe('RokuDeploy', () => { }); it('works for negated strings', () => { - expect(rokuDeploy['normalizeFilesArray']([ + expect(util['normalizeFilesArray']([ '!.git' ])).to.eql([ '!.git' @@ -2184,7 +2537,7 @@ describe('RokuDeploy', () => { }); it('skips falsey and bogus entries', () => { - expect(rokuDeploy['normalizeFilesArray']([ + expect(util['normalizeFilesArray']([ '', 'manifest', false, @@ -2196,7 +2549,7 @@ describe('RokuDeploy', () => { }); it('works for {src:string} objects', () => { - expect(rokuDeploy['normalizeFilesArray']([ + expect(util['normalizeFilesArray']([ { src: 'manifest' } @@ -2207,7 +2560,7 @@ describe('RokuDeploy', () => { }); it('works for {src:string[]} objects', () => { - expect(rokuDeploy['normalizeFilesArray']([ + expect(util['normalizeFilesArray']([ { src: [ 'manifest', @@ -2218,78 +2571,32 @@ describe('RokuDeploy', () => { src: 'manifest', dest: undefined }, { - src: s`source/main.brs`, + src: sp`source/main.brs`, dest: undefined }]); }); it('retains dest option', () => { - expect(rokuDeploy['normalizeFilesArray']([ + expect(util['normalizeFilesArray']([ { src: 'source/config.dev.brs', dest: 'source/config.brs' } ])).to.eql([{ - src: s`source/config.dev.brs`, + src: sp`source/config.dev.brs`, dest: s`source/config.brs` }]); }); it('throws when encountering invalid entries', () => { - expect(() => rokuDeploy['normalizeFilesArray']([true])).to.throw(); - expect(() => rokuDeploy['normalizeFilesArray']([/asdf/])).to.throw(); - expect(() => rokuDeploy['normalizeFilesArray']([new Date()])).to.throw(); - expect(() => rokuDeploy['normalizeFilesArray']([1])).to.throw(); - expect(() => rokuDeploy['normalizeFilesArray']([{ src: true }])).to.throw(); - expect(() => rokuDeploy['normalizeFilesArray']([{ src: /asdf/ }])).to.throw(); - expect(() => rokuDeploy['normalizeFilesArray']([{ src: new Date() }])).to.throw(); - expect(() => rokuDeploy['normalizeFilesArray']([{ src: 1 }])).to.throw(); - }); - }); - - describe('deploy', () => { - it('does the whole migration', async () => { - mockDoPostRequest(); - - writeFiles(rootDir, ['manifest']); - - let result = await rokuDeploy.deploy(options); - expect(result).not.to.be.undefined; - }); - - it('continues with deploy if deleteInstalledChannel fails', async () => { - sinon.stub(rokuDeploy, 'deleteInstalledChannel').returns( - Promise.reject( - new Error('failed') - ) - ); - mockDoPostRequest(); - let result = await rokuDeploy.deploy({ - ...options, - //something in the previous test is locking the default output zip file. We should fix that at some point... - outDir: s`${tempDir}/test1` - }); - expect(result).not.to.be.undefined; - }); - - it('should delete installed channel if requested', async () => { - const spy = sinon.spy(rokuDeploy, 'deleteInstalledChannel'); - options.deleteInstalledChannel = true; - mockDoPostRequest(); - - await rokuDeploy.deploy(options); - - expect(spy.called).to.equal(true); - }); - - it('should not delete installed channel if not requested', async () => { - const spy = sinon.spy(rokuDeploy, 'deleteInstalledChannel'); - options.deleteInstalledChannel = false; - mockDoPostRequest(); - - await rokuDeploy.deploy(options); - - expect(spy.notCalled).to.equal(true); + expect(() => util['normalizeFilesArray']([true])).to.throw(); + expect(() => util['normalizeFilesArray']([/asdf/])).to.throw(); + expect(() => util['normalizeFilesArray']([new Date()])).to.throw(); + expect(() => util['normalizeFilesArray']([1])).to.throw(); + expect(() => util['normalizeFilesArray']([{ src: true }])).to.throw(); + expect(() => util['normalizeFilesArray']([{ src: /asdf/ }])).to.throw(); + expect(() => util['normalizeFilesArray']([{ src: new Date() }])).to.throw(); + expect(() => util['normalizeFilesArray']([{ src: 1 }])).to.throw(); }); }); @@ -2350,12 +2657,15 @@ describe('RokuDeploy', () => { it('attempts to delete any installed dev channel on the device', async () => { mockDoPostRequest(); - let result = await rokuDeploy.deleteInstalledChannel(options); + let result = await rokuDeploy.deleteDevChannel({ + host: '1.2.3.4', + password: 'password' + }); expect(result).not.to.be.undefined; }); }); - describe('takeScreenshot', () => { + describe('captureScreenshot', () => { let onHandler: any; let screenshotAddress: any; @@ -2398,19 +2708,19 @@ describe('RokuDeploy', () => { `); mockDoPostRequest(body); - await expectThrowsAsync(rokuDeploy.takeScreenshot({ host: options.host, password: options.password })); + await expectThrowsAsync(rokuDeploy.captureScreenshot({ host: options.host, password: 'password' })); }); it('throws when there is no response body', async () => { // missing body mockDoPostRequest(null); - await expectThrowsAsync(rokuDeploy.takeScreenshot({ host: options.host, password: options.password })); + await expectThrowsAsync(rokuDeploy.captureScreenshot({ host: options.host, password: 'password' })); }); it('throws when there is an empty response body', async () => { // empty body mockDoPostRequest(); - await expectThrowsAsync(rokuDeploy.takeScreenshot({ host: options.host, password: options.password })); + await expectThrowsAsync(rokuDeploy.captureScreenshot({ host: options.host, password: 'password' })); }); it('throws when there is an error downloading the image from device', async () => { @@ -2431,7 +2741,7 @@ describe('RokuDeploy', () => { }; mockDoPostRequest(body); - await expectThrowsAsync(rokuDeploy.takeScreenshot({ host: options.host, password: options.password })); + await expectThrowsAsync(rokuDeploy.captureScreenshot({ host: options.host, password: 'password' })); }); it('handles the device returning a png', async () => { @@ -2452,7 +2762,7 @@ describe('RokuDeploy', () => { }; mockDoPostRequest(body); - let result = await rokuDeploy.takeScreenshot({ host: options.host, password: options.password }); + let result = await rokuDeploy.captureScreenshot({ host: options.host, password: 'password' }); expect(result).not.to.be.undefined; expect(path.extname(result)).to.equal('.png'); expect(fsExtra.existsSync(result)); @@ -2476,7 +2786,7 @@ describe('RokuDeploy', () => { }; mockDoPostRequest(body); - let result = await rokuDeploy.takeScreenshot({ host: options.host, password: options.password }); + let result = await rokuDeploy.captureScreenshot({ host: options.host, password: 'password' }); expect(result).not.to.be.undefined; expect(path.extname(result)).to.equal('.jpg'); expect(fsExtra.existsSync(result)); @@ -2500,7 +2810,7 @@ describe('RokuDeploy', () => { }; mockDoPostRequest(body); - let result = await rokuDeploy.takeScreenshot({ host: options.host, password: options.password, outDir: `${tempDir}/myScreenShots` }); + let result = await rokuDeploy.captureScreenshot({ host: options.host, password: 'password', screenshotDir: `${tempDir}/myScreenShots` }); expect(result).not.to.be.undefined; expect(util.standardizePath(`${tempDir}/myScreenShots`)).to.equal(path.dirname(result)); expect(fsExtra.existsSync(result)); @@ -2524,7 +2834,7 @@ describe('RokuDeploy', () => { }; mockDoPostRequest(body); - let result = await rokuDeploy.takeScreenshot({ host: options.host, password: options.password, outDir: tempDir, outFile: 'my' }); + let result = await rokuDeploy.captureScreenshot({ host: options.host, password: 'password', screenshotDir: tempDir, screenshotFile: 'my' }); expect(result).not.to.be.undefined; expect(util.standardizePath(tempDir)).to.equal(path.dirname(result)); expect(fsExtra.existsSync(path.join(tempDir, 'my.png'))); @@ -2548,7 +2858,7 @@ describe('RokuDeploy', () => { }; mockDoPostRequest(body); - let result = await rokuDeploy.takeScreenshot({ host: options.host, password: options.password, outDir: tempDir, outFile: 'my.jpg' }); + let result = await rokuDeploy.captureScreenshot({ host: options.host, password: 'password', screenshotDir: tempDir, screenshotFile: 'my.jpg' }); expect(result).not.to.be.undefined; expect(util.standardizePath(tempDir)).to.equal(path.dirname(result)); expect(fsExtra.existsSync(path.join(tempDir, 'my.jpg.png'))); @@ -2572,7 +2882,7 @@ describe('RokuDeploy', () => { }; mockDoPostRequest(body); - let result = await rokuDeploy.takeScreenshot({ host: options.host, password: options.password }); + let result = await rokuDeploy.captureScreenshot({ host: options.host, password: 'password' }); expect(result).not.to.be.undefined; expect(fsExtra.existsSync(result)); }); @@ -2595,67 +2905,23 @@ describe('RokuDeploy', () => { }; mockDoPostRequest(body); - let result = await rokuDeploy.takeScreenshot({ host: options.host, password: options.password, outFile: 'myFile' }); + let result = await rokuDeploy.captureScreenshot({ host: options.host, password: 'password', screenshotFile: 'myFile' }); expect(result).not.to.be.undefined; expect(path.basename(result)).to.equal('myFile.jpg'); expect(fsExtra.existsSync(result)); }); }); - describe('zipFolder', () => { + describe('makeZip', () => { //this is mainly done to hit 100% coverage, but why not ensure the errors are handled properly? :D it('rejects the promise when an error occurs', async () => { //zip path doesn't exist await assertThrowsAsync(async () => { - sinon.stub(fsExtra, 'outputFile').callsFake(() => { + sinon.stub(fsExtra, 'writeFile').callsFake(() => { throw new Error(); }); - await rokuDeploy.zipFolder('source', '.tmp/some/zip/path/that/does/not/exist'); - }); - }); - - it('allows modification of file contents with callback', async () => { - writeFiles(rootDir, [ - 'components/components/Loader/Loader.brs', - 'images/splash_hd.jpg', - 'source/main.brs', - 'manifest' - ]); - const stageFolder = path.join(tempDir, 'testProject'); - fsExtra.ensureDirSync(stageFolder); - const files = [ - 'components/components/Loader/Loader.brs', - 'images/splash_hd.jpg', - 'source/main.brs', - 'manifest' - ]; - for (const file of files) { - fsExtra.copySync(path.join(options.rootDir, file), path.join(stageFolder, file)); - } - - const outputZipPath = path.join(tempDir, 'output.zip'); - const addedManifestLine = 'bs_libs_required=roku_ads_lib'; - await rokuDeploy.zipFolder(stageFolder, outputZipPath, (file, data) => { - if (file.dest === 'manifest') { - let manifestContents = data.toString(); - manifestContents += addedManifestLine; - data = Buffer.from(manifestContents, 'utf8'); - } - return data; + await rokuDeploy['makeZip']('source', '.tmp/some/zip/path/that/does/not/exist'); }); - - const data = fsExtra.readFileSync(outputZipPath); - const zip = await JSZip.loadAsync(data as any); - for (const file of files) { - const zipFileContents = await zip.file(file.toString()).async('string'); - const sourcePath = path.join(options.rootDir, file); - const incomingContents = fsExtra.readFileSync(sourcePath, 'utf8'); - if (file === 'manifest') { - expect(zipFileContents).to.contain(addedManifestLine); - } else { - expect(zipFileContents).to.equal(incomingContents); - } - } }); it('filters the folders before making the zip', async () => { @@ -2670,7 +2936,7 @@ describe('RokuDeploy', () => { writeFiles(stagingDir, files); const outputZipPath = path.join(tempDir, 'output.zip'); - await rokuDeploy.zipFolder(stagingDir, outputZipPath, null, ['**/*', '!**/*.map']); + await rokuDeploy['makeZip'](stagingDir, outputZipPath, ['**/*', '!**/*.map']); const data = fsExtra.readFileSync(outputZipPath); const zip = await JSZip.loadAsync(data as any); @@ -2686,74 +2952,73 @@ describe('RokuDeploy', () => { ].sort().filter(x => !x.endsWith('.map')) ); }); - }); - describe('parseManifest', () => { - it('correctly parses valid manifest', async () => { - fsExtra.outputFileSync(`${rootDir}/manifest`, `title=AwesomeApp`); - let parsedManifest = await rokuDeploy.parseManifest(`${rootDir}/manifest`); - expect(parsedManifest.title).to.equal('AwesomeApp'); + it('should create zip in proper directory', async () => { + const outputZipPath = path.join(outDir, 'output.zip'); + await rokuDeploy['makeZip'](rootDir, outputZipPath, ['**/*', '!**/*.map']); + expectPathExists(rokuDeploy['getOutputZipFilePath']({ outDir: outDir, outFile: 'output.zip' })); }); - it('Throws our error message for a missing file', async () => { - await expectThrowsAsync( - rokuDeploy.parseManifest('invalid-path'), - `invalid-path does not exist` - ); - }); - }); + it('should only include the specified files', async () => { + await rokuDeploy.stage({ + files: [ + 'manifest' + ], + stagingDir: stagingDir, + rootDir: rootDir + }); - describe('parseManifestFromString', () => { - it('correctly parses valid manifest', () => { - let parsedManifest = rokuDeploy.parseManifestFromString(` - title=RokuDeployTestChannel - major_version=1 - minor_version=0 - build_version=0 - splash_screen_hd=pkg:/images/splash_hd.jpg - ui_resolutions=hd - bs_const=IS_DEV_BUILD=false - splash_color=#000000 - `); - expect(parsedManifest.title).to.equal('RokuDeployTestChannel'); - expect(parsedManifest.major_version).to.equal('1'); - expect(parsedManifest.minor_version).to.equal('0'); - expect(parsedManifest.build_version).to.equal('0'); - expect(parsedManifest.splash_screen_hd).to.equal('pkg:/images/splash_hd.jpg'); - expect(parsedManifest.ui_resolutions).to.equal('hd'); - expect(parsedManifest.bs_const).to.equal('IS_DEV_BUILD=false'); - expect(parsedManifest.splash_color).to.equal('#000000'); - }); - }); + await rokuDeploy.zip({ + stagingDir: stagingDir, + outDir: outDir + }); + const data = fsExtra.readFileSync(rokuDeploy['getOutputZipFilePath']({ outDir: outDir })); + const zip = await JSZip.loadAsync(data as any); - describe('stringifyManifest', () => { - it('correctly converts back to a valid manifest when lineNumber and keyIndexes are provided', () => { - expect( - rokuDeploy.stringifyManifest( - rokuDeploy.parseManifestFromString('major_version=3\nminor_version=4') - ) - ).to.equal( - 'major_version=3\nminor_version=4' - ); + const files = ['manifest']; + for (const file of files) { + const zipFileContents = await zip.file(file.toString()).async('string'); + const sourcePath = path.join(options.rootDir, file); + const incomingContents = fsExtra.readFileSync(sourcePath, 'utf8'); + expect(zipFileContents).to.equal(incomingContents); + } }); - it('correctly converts back to a valid manifest when lineNumber and keyIndexes are not provided', () => { - const parsed = rokuDeploy.parseManifestFromString('title=App\nmajor_version=3'); - delete parsed.keyIndexes; - delete parsed.lineCount; - let outputParsedManifest = rokuDeploy.parseManifestFromString( - rokuDeploy.stringifyManifest(parsed) - ); - expect(outputParsedManifest.title).to.equal('App'); - expect(outputParsedManifest.major_version).to.equal('3'); + it('generates full package with defaults', async () => { + const filePaths = writeFiles(rootDir, [ + 'components/components/Loader/Loader.brs', + 'images/splash_hd.jpg', + 'source/main.brs', + 'manifest' + ]); + options = { + files: filePaths, + stagingDir: stagingDir, + outDir: outDir, + rootDir: rootDir + }; + await rokuDeploy.stage(options); + await rokuDeploy.zip(options); + + const data = fsExtra.readFileSync(rokuDeploy['getOutputZipFilePath']({ outDir: outDir })); + const zip = await JSZip.loadAsync(data as any); + + for (const file of filePaths) { + const zipFileContents = await zip.file(file.toString())?.async('string'); + const sourcePath = path.join(options.rootDir, file); + const incomingContents = fsExtra.readFileSync(sourcePath, 'utf8'); + expect(zipFileContents).to.equal(incomingContents); + } }); }); describe('getFilePaths', () => { const otherProjectName = 'otherProject'; - const otherProjectDir = s`${rootDir}/../${otherProjectName}`; + const otherProjectDir = sp`${rootDir}/../${otherProjectName}`; //create baseline project structure beforeEach(() => { + rokuDeploy = new RokuDeploy(); + options = rokuDeploy.getOptions({}); fsExtra.ensureDirSync(`${rootDir}/components/emptyFolder`); writeFiles(rootDir, [ `manifest`, @@ -2917,6 +3182,33 @@ describe('RokuDeploy', () => { }]); }); + it('Finds folder using square brackets glob pattern', async () => { + fsExtra.outputFileSync(`${rootDir}/e/file.brs`, ''); + expect(await getFilePaths( + [ + '[test]/*' + ], + rootDir + )).to.eql([{ + src: s`${rootDir}/e/file.brs`, + dest: s`e/file.brs` + }]); + }); + + it('Finds folder with escaped square brackets glob pattern as name', async () => { + fsExtra.outputFileSync(`${rootDir}/[test]/file.brs`, ''); + fsExtra.outputFileSync(`${rootDir}/e/file.brs`, ''); + expect(await getFilePaths( + [ + '\\[test\\]/*' + ], + rootDir + )).to.eql([{ + src: s`${rootDir}/[test]/file.brs`, + dest: s`[test]/file.brs` + }]); + }); + it('throws exception when top-level strings reference files not under rootDir', async () => { writeFiles(otherProjectDir, [ 'manifest' @@ -3137,28 +3429,6 @@ describe('RokuDeploy', () => { }]); }); - it('works for other globs without dest', async () => { - expect(await getFilePaths([{ - src: `components/screen1/*creen1.brs` - }])).to.eql([{ - src: s`${rootDir}/components/screen1/screen1.brs`, - dest: s`screen1.brs` - }]); - }); - - it('skips directory folder names for other globs without dest', async () => { - expect(await getFilePaths([{ - //straight wildcard matches folder names too - src: `components/*` - }])).to.eql([{ - src: s`${rootDir}/components/component1.brs`, - dest: s`component1.brs` - }, { - src: s`${rootDir}/components/component1.xml`, - dest: s`component1.xml` - }]); - }); - it('applies negated patterns', async () => { writeFiles(rootDir, [ 'components/component1.brs', @@ -3235,19 +3505,17 @@ describe('RokuDeploy', () => { }); it('supports absolute paths from outside of the rootDir', async () => { - options = rokuDeploy.getOptions(options); - //dest not specified - expect(await rokuDeploy.getFilePaths([{ - src: s`${cwd}/README.md` + expect(await getFilePaths([{ + src: sp`${cwd}/README.md` }], options.rootDir)).to.eql([{ src: s`${cwd}/README.md`, dest: s`README.md` }]); //dest specified - expect(await rokuDeploy.getFilePaths([{ - src: path.join(cwd, 'README.md'), + expect(await getFilePaths([{ + src: sp`${cwd}/README.md`, dest: 'docs/README.md' }], options.rootDir)).to.eql([{ src: s`${cwd}/README.md`, @@ -3256,8 +3524,8 @@ describe('RokuDeploy', () => { let paths: any[]; - paths = await rokuDeploy.getFilePaths([{ - src: s`${cwd}/README.md`, + paths = await getFilePaths([{ + src: sp`${cwd}/README.md`, dest: s`docs/README.md` }], outDir); @@ -3268,8 +3536,8 @@ describe('RokuDeploy', () => { //top-level string paths pointing to files outside the root should thrown an exception await expectThrowsAsync(async () => { - paths = await rokuDeploy.getFilePaths([ - s`${cwd}/README.md` + paths = await getFilePaths([ + sp`${cwd}/README.md` ], outDir); }); }); @@ -3279,8 +3547,8 @@ describe('RokuDeploy', () => { 'README.md' ]); expect( - await rokuDeploy.getFilePaths([{ - src: path.join('..', 'README.md') + await getFilePaths([{ + src: sp`../README.md` }], rootDir) ).to.eql([{ src: s`${rootDir}/../README.md`, @@ -3288,8 +3556,8 @@ describe('RokuDeploy', () => { }]); expect( - await rokuDeploy.getFilePaths([{ - src: path.join('..', 'README.md'), + await getFilePaths([{ + src: sp`../README.md`, dest: 'docs/README.md' }], rootDir) ).to.eql([{ @@ -3303,18 +3571,18 @@ describe('RokuDeploy', () => { '../README.md' ]); await expectThrowsAsync( - rokuDeploy.getFilePaths([ - path.join('..', 'README.md') + getFilePaths([ + path.posix.join('..', 'README.md') ], outDir) ); }); it('supports overriding paths', async () => { - let paths = await rokuDeploy.getFilePaths([{ - src: s`${rootDir}/components/component1.brs`, + let paths = await getFilePaths([{ + src: sp`${rootDir}/components/component1.brs`, dest: 'comp1.brs' }, { - src: s`${rootDir}/components/screen1/screen1.brs`, + src: sp`${rootDir}/components/screen1/screen1.brs`, dest: 'comp1.brs' }], rootDir); expect(paths).to.be.lengthOf(1); @@ -3342,7 +3610,7 @@ describe('RokuDeploy', () => { dest: 'components/MainScene.brs' } ]; - let paths = await rokuDeploy.getFilePaths(files, thisRootDir); + let paths = await getFilePaths(files, thisRootDir); //the MainScene.brs file from source should NOT be included let mainSceneEntries = paths.filter(x => s`${x.dest}` === s`components/MainScene.brs`); @@ -3356,307 +3624,73 @@ describe('RokuDeploy', () => { await fsExtra.remove(s`${thisRootDir}/../`); } }); - }); - - describe('computeFileDestPath', () => { - it('treats {src;dest} without dest as a top-level string', () => { - expect( - rokuDeploy['computeFileDestPath'](s`${rootDir}/source/main.brs`, { src: s`source/main.brs` } as any, rootDir) - ).to.eql(s`source/main.brs`); - }); - }); - - describe('getDestPath', () => { - it('handles absolute paths properly', () => { - expect( - rokuDeploy.getDestPath( - s`${tempDir}/rootDir/source/main.bs`, - [{ - src: `${tempDir}/rootDir/source/main.bs`, - dest: 'source/standalone.brs' - }], - `${tempDir}/src/lsp/standalone-project-1` - ) - ).to.equal(s`source/standalone.brs`); - }); - - it('handles unrelated exclusions properly', () => { - expect( - rokuDeploy.getDestPath( - s`${rootDir}/components/comp1/comp1.brs`, - [ - '**/*', - '!exclude.me' - ], - rootDir - ) - ).to.equal(s`components/comp1/comp1.brs`); - }); - - it('finds dest path for top-level path', () => { - expect( - rokuDeploy.getDestPath( - s`${rootDir}/components/comp1/comp1.brs`, - ['components/**/*'], - rootDir - ) - ).to.equal(s`components/comp1/comp1.brs`); - }); - it('does not find dest path for non-matched top-level path', () => { + it('maintains original file path', async () => { + fsExtra.outputFileSync(`${rootDir}/components/CustomButton.brs`, ''); expect( - rokuDeploy.getDestPath( - s`${rootDir}/source/main.brs`, - ['components/**/*'], - rootDir - ) - ).to.be.undefined; + await getFilePaths([ + 'components/CustomButton.brs' + ], rootDir) + ).to.eql([{ + src: s`${rootDir}/components/CustomButton.brs`, + dest: s`components/CustomButton.brs` + }]); }); - it('excludes a file that is negated', () => { + it('correctly assumes file path if not given', async () => { + fsExtra.outputFileSync(`${rootDir}/components/CustomButton.brs`, ''); expect( - rokuDeploy.getDestPath( - s`${rootDir}/source/main.brs`, - [ - 'source/**/*', - '!source/main.brs' - ], - rootDir - ) - ).to.be.undefined; + (await getFilePaths([ + { src: 'components/*' } + ], rootDir)).sort((a, b) => a.src.localeCompare(b.src)) + ).to.eql([{ + src: s`${rootDir}/components/component1.brs`, + dest: s`components/component1.brs` + }, { + src: s`${rootDir}/components/component1.xml`, + dest: s`components/component1.xml` + }, { + src: s`${rootDir}/components/CustomButton.brs`, + dest: s`components/CustomButton.brs` + }]); }); + }); - it('excludes file from non-rootdir top-level pattern', () => { - expect( - rokuDeploy.getDestPath( - s`${rootDir}/../externalDir/source/main.brs`, - [ - '!../externalDir/**/*' - ], - rootDir - ) - ).to.be.undefined; + describe('parseManifest', () => { + it('correctly parses valid manifest', async () => { + fsExtra.outputFileSync(`${rootDir}/manifest`, `title=AwesomeApp`); + let parsedManifest = await rokuDeploy['parseManifest'](`${rootDir}/manifest`); + expect(parsedManifest.title).to.equal('AwesomeApp'); }); - it('excludes a file that is negated in src;dest;', () => { - expect( - rokuDeploy.getDestPath( - s`${rootDir}/source/main.brs`, - [ - 'source/**/*', - { - src: '!source/main.brs' - } - ], - rootDir - ) - ).to.be.undefined; + it('Throws our error message for a missing file', async () => { + await expectThrowsAsync( + rokuDeploy['parseManifest']('invalid-path'), + `invalid-path does not exist` + ); }); + }); - it('works for brighterscript files', () => { - let destPath = rokuDeploy.getDestPath( - util.standardizePath(`${cwd}/src/source/main.bs`), - [ - 'manifest', - 'source/**/*.bs' - ], - s`${cwd}/src` - ); - expect(s`${destPath}`).to.equal(s`source/main.bs`); - }); - - it('excludes a file found outside the root dir', () => { - expect( - rokuDeploy.getDestPath( - s`${rootDir}/../source/main.brs`, - [ - '../source/**/*' - ], - rootDir - ) - ).to.be.undefined; - }); - }); - - describe('normalizeRootDir', () => { - it('handles falsey values', () => { - expect(rokuDeploy.normalizeRootDir(null)).to.equal(cwd); - expect(rokuDeploy.normalizeRootDir(undefined)).to.equal(cwd); - expect(rokuDeploy.normalizeRootDir('')).to.equal(cwd); - expect(rokuDeploy.normalizeRootDir(' ')).to.equal(cwd); - expect(rokuDeploy.normalizeRootDir('\t')).to.equal(cwd); - }); - - it('handles non-falsey values', () => { - expect(rokuDeploy.normalizeRootDir(cwd)).to.equal(cwd); - expect(rokuDeploy.normalizeRootDir('./')).to.equal(cwd); - expect(rokuDeploy.normalizeRootDir('./testProject')).to.equal(path.join(cwd, 'testProject')); - }); - }); - - describe('retrieveSignedPackage', () => { - let onHandler: any; - beforeEach(() => { - sinon.stub(rokuDeploy.fsExtra, 'ensureDir').callsFake(((pth: string, callback: (err: Error) => void) => { - //do nothing, assume the dir gets created - }) as any); - - //intercept the http request - sinon.stub(request, 'get').callsFake(() => { - let req: any = { - on: (event, callback) => { - process.nextTick(() => { - onHandler(event, callback); - }); - return req; - }, - pipe: async () => { - //if a write stream gets created, write some stuff and close it - const writeStream = await writeStreamPromise; - writeStream.write('test'); - writeStream.close(); - } - }; - return req; - }); - }); - - it('returns a pkg file path on success', async () => { - onHandler = (event, callback) => { - if (event === 'response') { - callback({ - statusCode: 200 - }); - } - }; - let pkgFilePath = await rokuDeploy.retrieveSignedPackage('path_to_pkg', { - outFile: 'roku-deploy-test' - }); - expect(pkgFilePath).to.equal(path.join(process.cwd(), 'out', 'roku-deploy-test.pkg')); - }); - - it('returns a pkg file path on success', async () => { - //the write stream should return null, which causes a specific branch to be executed - createWriteStreamStub.callsFake(() => { - return null; - }); - - onHandler = (event, callback) => { - if (event === 'response') { - callback({ - statusCode: 200 - }); - } - }; - - let error: Error; - try { - await rokuDeploy.retrieveSignedPackage('path_to_pkg', { - outFile: 'roku-deploy-test' - }); - } catch (e) { - error = e as any; - } - expect(error.message.startsWith('Unable to create write stream for')).to.be.true; - }); - - it('throws when error in request is encountered', async () => { - onHandler = (event, callback) => { - if (event === 'error') { - callback(new Error('Some error')); - } - }; - await expectThrowsAsync( - rokuDeploy.retrieveSignedPackage('path_to_pkg', { - outFile: 'roku-deploy-test' - }), - 'Some error' - ); - }); - - it('throws when status code is non 200', async () => { - onHandler = (event, callback) => { - if (event === 'response') { - callback({ - statusCode: 500 - }); - } - }; - await expectThrowsAsync( - rokuDeploy.retrieveSignedPackage('path_to_pkg', { - outFile: 'roku-deploy-test' - }), - 'Invalid response code: 500' - ); - }); - }); - - describe('prepublishToStaging', () => { - it('is resilient to file system errors', async () => { - let copy = rokuDeploy.fsExtra.copy; - let count = 0; - - //mock writeFile so we can throw a few errors during the test - sinon.stub(rokuDeploy.fsExtra, 'copy').callsFake((...args) => { - count += 1; - //fail a few times - if (count < 5) { - throw new Error('fake error thrown as part of the unit test'); - } else { - return copy.apply(rokuDeploy.fsExtra, args); - } - }); - - //override the retry milliseconds to make test run faster - let orig = util.tryRepeatAsync.bind(util); - sinon.stub(util, 'tryRepeatAsync').callsFake(async (...args) => { - return orig(args[0], args[1], 0); - }); - - fsExtra.outputFileSync(`${rootDir}/source/main.brs`, ''); - - await rokuDeploy.prepublishToStaging({ - ...options, - files: [ - 'source/main.brs' - ] - }); - expectPathExists(s`${stagingDir}/source/main.brs`); - expect(count).to.be.greaterThan(4); - }); - - it('throws underlying error after the max fs error threshold is reached', async () => { - let copy = rokuDeploy.fsExtra.copy; - let count = 0; - - //mock writeFile so we can throw a few errors during the test - sinon.stub(rokuDeploy.fsExtra, 'copy').callsFake((...args) => { - count += 1; - //fail a few times - if (count < 15) { - throw new Error('fake error thrown as part of the unit test'); - } else { - return copy.apply(rokuDeploy.fsExtra, args); - } - }); - - //override the timeout for tryRepeatAsync so this test runs faster - let orig = util.tryRepeatAsync.bind(util); - sinon.stub(util, 'tryRepeatAsync').callsFake(async (...args) => { - return orig(args[0], args[1], 0); - }); - - fsExtra.outputFileSync(`${rootDir}/source/main.brs`, ''); - await expectThrowsAsync( - rokuDeploy.prepublishToStaging({ - rootDir: rootDir, - stagingDir: stagingDir, - files: [ - 'source/main.brs' - ] - }), - 'fake error thrown as part of the unit test' - ); + describe('parseManifestFromString', () => { + it('correctly parses valid manifest', () => { + let parsedManifest = rokuDeploy['parseManifestFromString'](` + title=RokuDeployTestChannel + major_version=1 + minor_version=0 + build_version=0 + splash_screen_hd=pkg:/images/splash_hd.jpg + ui_resolutions=hd + bs_const=IS_DEV_BUILD=false + splash_color=#000000 + `); + expect(parsedManifest.title).to.equal('RokuDeployTestChannel'); + expect(parsedManifest.major_version).to.equal('1'); + expect(parsedManifest.minor_version).to.equal('0'); + expect(parsedManifest.build_version).to.equal('0'); + expect(parsedManifest.splash_screen_hd).to.equal('pkg:/images/splash_hd.jpg'); + expect(parsedManifest.ui_resolutions).to.equal('hd'); + expect(parsedManifest.bs_const).to.equal('IS_DEV_BUILD=false'); + expect(parsedManifest.splash_color).to.equal('#000000'); }); }); @@ -3679,36 +3713,6 @@ describe('RokuDeploy', () => { }); describe('getOptions', () => { - it('supports deprecated stagingFolderPath option', () => { - sinon.stub(fsExtra, 'existsSync').callsFake((filePath) => { - return false; - }); - expect( - rokuDeploy.getOptions({ stagingFolderPath: 'staging-folder-path' }).stagingDir - ).to.eql(s`${cwd}/staging-folder-path`); - expect( - rokuDeploy.getOptions({ stagingFolderPath: 'staging-folder-path', stagingDir: 'staging-dir' }).stagingDir - ).to.eql(s`${cwd}/staging-dir`); - expect( - rokuDeploy.getOptions({ stagingFolderPath: 'staging-folder-path' }).stagingFolderPath - ).to.eql(s`${cwd}/staging-folder-path`); - }); - - it('supports deprecated retainStagingFolder option', () => { - sinon.stub(fsExtra, 'existsSync').callsFake((filePath) => { - return false; - }); - expect( - rokuDeploy.getOptions({ retainStagingFolder: true }).retainStagingDir - ).to.be.true; - expect( - rokuDeploy.getOptions({ retainStagingFolder: true, retainStagingDir: false }).retainStagingDir - ).to.be.false; - expect( - rokuDeploy.getOptions({ retainStagingFolder: true, retainStagingDir: false }).retainStagingFolder - ).to.be.false; - }); - it('calling with no parameters works', () => { sinon.stub(fsExtra, 'existsSync').callsFake((filePath) => { return false; @@ -3726,89 +3730,23 @@ describe('RokuDeploy', () => { }); it('works when passing in stagingDir', () => { - sinon.stub(fsExtra, 'existsSync').callsFake((filePath) => { - return false; - }); options = rokuDeploy.getOptions({ stagingDir: './staging-dir' }); expect(options.stagingDir.endsWith('staging-dir')).to.be.true; }); - it('works when loading stagingDir from rokudeploy.json', () => { - sinon.stub(fsExtra, 'existsSync').callsFake((filePath) => { - return true; - }); - sinon.stub(fsExtra, 'readFileSync').returns(` - { - "stagingDir": "./staging-dir" - } - `); - options = rokuDeploy.getOptions(); - expect(options.stagingDir.endsWith('staging-dir')).to.be.true; - }); - - it('supports jsonc for roku-deploy.json', () => { - sinon.stub(fsExtra, 'existsSync').callsFake((filePath) => { - return (filePath as string).endsWith('rokudeploy.json'); - }); - sinon.stub(fsExtra, 'readFileSync').returns(` - //leading comment - { - //inner comment - "rootDir": "src" //trailing comment - } - //trailing comment - `); - options = rokuDeploy.getOptions(undefined); - expect(options.rootDir).to.equal(path.join(process.cwd(), 'src')); - }); - - it('supports jsonc for bsconfig.json', () => { - sinon.stub(fsExtra, 'existsSync').callsFake((filePath) => { - return (filePath as string).endsWith('bsconfig.json'); - }); - sinon.stub(fsExtra, 'readFileSync').returns(` - //leading comment - { - //inner comment - "rootDir": "src" //trailing comment - } - //trailing comment - `); - options = rokuDeploy.getOptions(undefined); - expect(options.rootDir).to.equal(path.join(process.cwd(), 'src')); - }); - - it('catches invalid json with jsonc parser', () => { - sinon.stub(fsExtra, 'existsSync').callsFake((filePath) => { - return (filePath as string).endsWith('bsconfig.json'); - }); - sinon.stub(fsExtra, 'readFileSync').returns(` - { - "rootDir": "src" - `); - let ex; - try { - rokuDeploy.getOptions(undefined); - } catch (e) { - ex = e; - } - expect(ex).to.exist; - expect(ex.message.startsWith('Error parsing')).to.be.true; - }); - it('does not error when no parameter provided', () => { expect(rokuDeploy.getOptions(undefined)).to.exist; }); - describe('deleteInstalledChannel', () => { + describe('deleteDevChannel', () => { it('defaults to true', () => { - expect(rokuDeploy.getOptions({}).deleteInstalledChannel).to.equal(true); + expect(rokuDeploy.getOptions({}).deleteDevChannel).to.equal(true); }); it('can be overridden', () => { - expect(rokuDeploy.getOptions({ deleteInstalledChannel: false }).deleteInstalledChannel).to.equal(false); + expect(rokuDeploy.getOptions({ deleteDevChannel: false }).deleteDevChannel).to.equal(false); }); }); @@ -3834,7 +3772,7 @@ describe('RokuDeploy', () => { }); }); - describe('config file', () => { + describe('default options', () => { beforeEach(() => { process.chdir(rootDir); }); @@ -3843,46 +3781,122 @@ describe('RokuDeploy', () => { expect(rokuDeploy.getOptions().outFile).to.equal('roku-deploy'); }); - it('if rokudeploy.json config file is available it should use those values instead of the default', () => { - fsExtra.writeJsonSync(s`${rootDir}/rokudeploy.json`, { outFile: 'rokudeploy-outfile' }); - expect(rokuDeploy.getOptions().outFile).to.equal('rokudeploy-outfile'); + it('if runtime options are provided, they should override any default options', () => { + expect(rokuDeploy.getOptions({ + outFile: 'runtime-outfile' + }).outFile).to.equal('runtime-outfile'); }); + }); - it('if bsconfig.json config file is available it should use those values instead of the default', () => { - fsExtra.writeJsonSync(`${rootDir}/bsconfig.json`, { outFile: 'bsconfig-outfile' }); - expect(rokuDeploy.getOptions().outFile).to.equal('bsconfig-outfile'); + describe('cwd', () => { + it('Only has cwd supplied', () => { + options = rokuDeploy.getOptions({ + cwd: rootDir + }); + + expect(options.rootDir).to.equal(s`${rootDir}`); + expect(options.outDir).to.equal(s`${rootDir}/out`); + expect(options.screenshotDir.endsWith(s`/roku-deploy/screenshots`)).to.be.true; }); - it('if rokudeploy.json config file is available and bsconfig.json is also available it should use rokudeploy.json instead of bsconfig.json', () => { - fsExtra.outputJsonSync(`${rootDir}/bsconfig.json`, { outFile: 'bsconfig-outfile' }); - fsExtra.outputJsonSync(`${rootDir}/rokudeploy.json`, { outFile: 'rokudeploy-outfile' }); - expect(rokuDeploy.getOptions().outFile).to.equal('rokudeploy-outfile'); + it('has no cwd supplied', () => { + options = rokuDeploy.getOptions({}); + + expect(options.rootDir).to.equal(s`${__dirname}/..`); + expect(options.outDir).to.equal(s`${__dirname}/../out`); + expect(options.screenshotDir.endsWith(s`/roku-deploy/screenshots`)).to.be.true; }); - it('if runtime options are provided, they should override any existing config file options', () => { - fsExtra.writeJsonSync(`${rootDir}/bsconfig.json`, { outFile: 'bsconfig-outfile' }); - fsExtra.writeJsonSync(`${rootDir}/rokudeploy.json`, { outFile: 'rokudeploy-outfile' }); - expect(rokuDeploy.getOptions({ - outFile: 'runtime-outfile' - }).outFile).to.equal('runtime-outfile'); + it('has no cwd supplied but screenshotDir is supplied', () => { + options = rokuDeploy.getOptions({ + screenshotDir: './screenshotDir' + }); + + expect(options.rootDir).to.equal(s`${__dirname}/..`); + expect(options.outDir).to.equal(s`${__dirname}/../out`); + expect(options.screenshotDir).to.equal(s`${__dirname}/../screenshotDir`); }); - it('if runtime config should override any existing config file options', () => { - fsExtra.writeJsonSync(s`${rootDir}/rokudeploy.json`, { outFile: 'rokudeploy-outfile' }); - fsExtra.writeJsonSync(s`${rootDir}/bsconfig`, { outFile: 'rokudeploy-outfile' }); + it('screenshotDir is built relative to cwd', () => { + options = rokuDeploy.getOptions({ + cwd: tempDir, + screenshotDir: './screenshotDir' + }); - fsExtra.writeJsonSync(s`${rootDir}/brsconfig.json`, { outFile: 'project-config-outfile' }); - options = { - project: 'brsconfig.json' - }; - expect(rokuDeploy.getOptions(options).outFile).to.equal('project-config-outfile'); + expect(options.screenshotDir).to.equal(s`${tempDir}/screenshotDir`); }); }); + }); - describe('getToFile', () => { + describe('checkRequiredOptions', () => { + async function testRequiredOptions(action: string, requiredOptions: Partial, testedOption: string) { + const newOptions = { ...requiredOptions }; + delete newOptions[testedOption]; + await expectThrowsAsync(async () => { + await rokuDeploy[action](newOptions); + }, `Missing required option: ${testedOption}`); + } + + it('throws error when sendKeyEvent is missing required options', async () => { + const requiredOptions: Partial = { host: '1.2.3.4', key: 'up' }; + await testRequiredOptions('sendKeyEvent', requiredOptions, 'host'); + await testRequiredOptions('sendKeyEvent', requiredOptions, 'key'); + }); + + it('throws error when sideload is missing required options', async () => { + const requiredOptions: Partial = { host: '1.2.3.4', password: 'abcd' }; + await testRequiredOptions('sideload', requiredOptions, 'host'); + await testRequiredOptions('sideload', requiredOptions, 'password'); + }); + + it('throws error when convertToSquashfs is missing required options', async () => { + const requiredOptions: Partial = { host: '1.2.3.4', password: 'abcd' }; + await testRequiredOptions('convertToSquashfs', requiredOptions, 'host'); + await testRequiredOptions('convertToSquashfs', requiredOptions, 'password'); + }); + + it('throws error when rekeyDevice is missing required options', async () => { + const requiredOptions: Partial = { host: '1.2.3.4', password: 'abcd', rekeySignedPackage: 'abcd', signingPassword: 'abcd' }; + await testRequiredOptions('rekeyDevice', requiredOptions, 'host'); + await testRequiredOptions('rekeyDevice', requiredOptions, 'password'); + await testRequiredOptions('rekeyDevice', requiredOptions, 'rekeySignedPackage'); + await testRequiredOptions('rekeyDevice', requiredOptions, 'signingPassword'); + }); + + it('throws error when createSignedPackage is missing required options', async () => { + const requiredOptions: Partial = { host: '1.2.3.4', password: 'abcd', signingPassword: 'abcd' }; + await testRequiredOptions('createSignedPackage', requiredOptions, 'host'); + await testRequiredOptions('createSignedPackage', requiredOptions, 'password'); + await testRequiredOptions('createSignedPackage', requiredOptions, 'signingPassword'); + }); + + it('throws error when deleteDevChannel is missing required options', async () => { + const requiredOptions: Partial = { host: '1.2.3.4', password: 'abcd' }; + await testRequiredOptions('deleteDevChannel', requiredOptions, 'host'); + await testRequiredOptions('deleteDevChannel', requiredOptions, 'password'); + }); + + it('throws error when captureScreenshot is missing required options', async () => { + const requiredOptions: Partial = { host: '1.2.3.4', password: 'abcd' }; + await testRequiredOptions('captureScreenshot', requiredOptions, 'host'); + await testRequiredOptions('captureScreenshot', requiredOptions, 'password'); + }); + + it('throws error when getDeviceInfo is missing required options', async () => { + const requiredOptions: Partial = { host: '1.2.3.4' }; + await testRequiredOptions('getDeviceInfo', requiredOptions, 'host'); + }); + + it('throws error when getDevId is missing required options', async () => { + const requiredOptions: Partial = { host: '1.2.3.4' }; + await testRequiredOptions('getDevId', requiredOptions, 'host'); + }); + }); + + describe('downloadFile', () => { it('waits for the write stream to finish writing before resolving', async () => { - let getToFileIsResolved = false; + let downloadFileIsResolved = false; let requestCalled = q.defer(); let onResponse = q.defer<(res) => any>(); @@ -3904,70 +3918,25 @@ describe('RokuDeploy', () => { return req; }); - const finalPromise = rokuDeploy['getToFile']({}, s`${tempDir}/out/something.txt`).then(() => { - getToFileIsResolved = true; + const finalPromise = rokuDeploy['downloadFile']({}, s`${tempDir}/out/something.txt`).then(() => { + downloadFileIsResolved = true; }); await requestCalled.promise; - expect(getToFileIsResolved).to.be.false; + expect(downloadFileIsResolved).to.be.false; const callback = await onResponse.promise; callback({ statusCode: 200 }); await util.sleep(10); - expect(getToFileIsResolved).to.be.false; + expect(downloadFileIsResolved).to.be.false; const writeStream = await writeStreamPromise; writeStream.write('test'); writeStream.close(); await finalPromise; - expect(getToFileIsResolved).to.be.true; - }); - }); - - describe('deployAndSignPackage', () => { - beforeEach(() => { - //pretend the deploy worked - sinon.stub(rokuDeploy, 'deploy').returns(Promise.resolve(null)); - //pretend the sign worked - sinon.stub(rokuDeploy, 'signExistingPackage').returns(Promise.resolve(null)); - //pretend fetching the signed package worked - sinon.stub(rokuDeploy, 'retrieveSignedPackage').returns(Promise.resolve('some_local_path')); - }); - - it('succeeds and does proper things with staging folder', async () => { - let stub = sinon.stub(rokuDeploy['fsExtra'], 'remove').returns(Promise.resolve() as any); - - //this should not fail - let pkgFilePath = await rokuDeploy.deployAndSignPackage({ - retainStagingDir: false - }); - - //the return value should equal what retrieveSignedPackage returned. - expect(pkgFilePath).to.equal('some_local_path'); - - //fsExtra.remove should have been called - expect(stub.getCalls()).to.be.lengthOf(1); - - //call it again, but specify true for retainStagingDir - await rokuDeploy.deployAndSignPackage({ - retainStagingDir: true - }); - //call count should NOT increase - expect(stub.getCalls()).to.be.lengthOf(1); - - //call it again, but don't specify retainStagingDir at all (it should default to FALSE) - await rokuDeploy.deployAndSignPackage({}); - //call count should NOT increase - expect(stub.getCalls()).to.be.lengthOf(2); - }); - - it('converts to squashfs if we request it to', async () => { - options.convertToSquashfs = true; - let stub = sinon.stub(rokuDeploy, 'convertToSquashfs').returns(Promise.resolve(null)); - await rokuDeploy.deployAndSignPackage(options); - expect(stub.getCalls()).to.be.lengthOf(1); + expect(downloadFileIsResolved).to.be.true; }); }); diff --git a/src/RokuDeploy.ts b/src/RokuDeploy.ts index cdc7106..e97f369 100644 --- a/src/RokuDeploy.ts +++ b/src/RokuDeploy.ts @@ -1,118 +1,71 @@ import * as path from 'path'; -import * as _fsExtra from 'fs-extra'; +import * as fsExtra from 'fs-extra'; +import type { WriteStream, ReadStream } from 'fs-extra'; import * as r from 'postman-request'; import type * as requestType from 'request'; const request = r as typeof requestType; import * as JSZip from 'jszip'; -import * as dateformat from 'dateformat'; import * as errors from './Errors'; -import * as isGlob from 'is-glob'; -import * as picomatch from 'picomatch'; import * as xml2js from 'xml2js'; -import type { ParseError } from 'jsonc-parser'; -import { parse as parseJsonc, printParseErrorCode } from 'jsonc-parser'; +import { parse as parseJsonc } from 'jsonc-parser'; import { util } from './util'; import type { RokuDeployOptions, FileEntry } from './RokuDeployOptions'; -import { Logger, LogLevel } from './Logger'; -import * as tempDir from 'temp-dir'; +import { logger } from '@rokucommunity/logger'; import * as dayjs from 'dayjs'; import * as lodash from 'lodash'; import type { DeviceInfo, DeviceInfoRaw } from './DeviceInfo'; +import * as tempDir from 'temp-dir'; import * as semver from 'semver'; export class RokuDeploy { - - constructor() { - this.logger = new Logger(); - } - - private logger: Logger; - //store the import on the class to make testing easier - - public fsExtra = _fsExtra; - - public screenshotDir = path.join(tempDir, '/roku-deploy/screenshots/'); - /** * Copies all of the referenced files to the staging folder * @param options */ - public async prepublishToStaging(options: RokuDeployOptions) { - options = this.getOptions(options); + public async stage(options: StageOptions) { + logger.info('Beginning to copy files to staging folder'); + options = this.getOptions(options) as any; //clean the staging directory - await this.fsExtra.remove(options.stagingDir); + await fsExtra.remove(options.stagingDir); //make sure the staging folder exists - await this.fsExtra.ensureDir(options.stagingDir); - await this.copyToStaging(options.files, options.stagingDir, options.rootDir); - return options.stagingDir; - } + await fsExtra.ensureDir(options.stagingDir); - /** - * Given an array of `FilesType`, normalize them each into a `StandardizedFileEntry`. - * Each entry in the array or inner `src` array will be extracted out into its own object. - * This makes it easier to reason about later on in the process. - * @param files - */ - public normalizeFilesArray(files: FileEntry[]) { - const result: Array = []; - - for (let i = 0; i < files.length; i++) { - let entry = files[i]; - //skip falsey and blank entries - if (!entry) { - continue; - - //string entries - } else if (typeof entry === 'string') { - result.push(entry); - - //objects with src: (string | string[]) - } else if ('src' in entry) { - //validate dest - if (entry.dest !== undefined && entry.dest !== null && typeof entry.dest !== 'string') { - throw new Error(`Invalid type for "dest" at index ${i} of files array`); - } + if (!await fsExtra.pathExists(options.rootDir)) { + throw new Error(`rootDir does not exist at "${options.rootDir}"`); + } - //objects with src: string - if (typeof entry.src === 'string') { - result.push({ - src: util.standardizePath(entry.src), - dest: util.standardizePath(entry.dest) - }); + let fileObjects = await this.getFilePaths(options.files, options.rootDir); + //copy all of the files + await Promise.all(fileObjects.map(async (fileObject) => { + let destFilePath = util.standardizePath(`${options.stagingDir}/${fileObject.dest}`); - //objects with src:string[] - } else if ('src' in entry && Array.isArray(entry.src)) { - //create a distinct entry for each item in the src array - for (let srcEntry of entry.src) { - result.push({ - src: util.standardizePath(srcEntry), - dest: util.standardizePath(entry.dest) - }); - } - } else { - throw new Error(`Invalid type for "src" at index ${i} of files array`); - } - } else { - throw new Error(`Invalid entry at index ${i} in files array`); - } - } + //make sure the containing folder exists + await fsExtra.ensureDir(path.dirname(destFilePath)); - return result; + //sometimes the copyfile action fails due to race conditions (normally to poorly constructed src;dest; objects with duplicate files in them + await util.tryRepeatAsync(async () => { + //copy the src item using the filesystem + await fsExtra.copy(fileObject.src, destFilePath, { + //copy the actual files that symlinks point to, not the symlinks themselves + dereference: true + }); + }, 10); + })); + logger.info('Relevant files copied to:', options.stagingDir); + return options.stagingDir; } /** * Given an already-populated staging folder, create a zip archive of it and copy it to the output folder * @param options */ - public async zipPackage(options: RokuDeployOptions) { - options = this.getOptions(options); - - //make sure the output folder exists - await this.fsExtra.ensureDir(options.outDir); + public async zip(options: ZipOptions) { + logger.info('Beginning to zip staging folder'); + options = this.getOptions(options) as any; - let zipFilePath = this.getOutputZipFilePath(options); + let zipFilePath = this.getOutputZipFilePath(options as any); //ensure the manifest file exists in the staging folder if (!await util.fileExistsCaseInsensitive(`${options.stagingDir}/manifest`)) { @@ -120,55 +73,45 @@ export class RokuDeploy { } //create a zip of the staging folder - await this.zipFolder(options.stagingDir, zipFilePath); - - //delete the staging folder unless told to retain it. - if (options.retainStagingDir !== true) { - await this.fsExtra.remove(options.stagingDir); - } + await this.makeZip(options.stagingDir, zipFilePath); + logger.info('Zip created at:', zipFilePath); } /** - * Create a zip folder containing all of the specified roku project files. - * @param options + * Given a path to a folder, zip up that folder and all of its contents + * @param srcFolder the folder that should be zipped + * @param zipFilePath the path to the zip that will be created + * @param files a files array used to filter the files from `srcFolder` */ - public async createPackage(options: RokuDeployOptions, beforeZipCallback?: (info: BeforeZipCallbackInfo) => Promise | void) { - options = this.getOptions(options); - - await this.prepublishToStaging(options); - - let manifestPath = util.standardizePath(`${options.stagingDir}/manifest`); - let parsedManifest = await this.parseManifest(manifestPath); - - if (options.incrementBuildNumber) { - let timestamp = dateformat(new Date(), 'yymmddHHMM'); - parsedManifest.build_version = timestamp; //eslint-disable-line camelcase - await this.fsExtra.outputFile(manifestPath, this.stringifyManifest(parsedManifest)); - } + private async makeZip(srcFolder: string, zipFilePath: string, files: FileEntry[] = ['**/*']) { + const filePaths = await this.getFilePaths(files, srcFolder); - if (beforeZipCallback) { - let info: BeforeZipCallbackInfo = { - manifestData: parsedManifest, - stagingFolderPath: options.stagingDir, - stagingDir: options.stagingDir - }; + const zip = new JSZip(); + // Allows us to wait until all are done before we build the zip + const promises = []; + for (const file of filePaths) { + const promise = fsExtra.readFile(file.src).then((data) => { + const ext = path.extname(file.dest).toLowerCase(); + let compression: 'DEFLATE' | 'STORE' = 'DEFLATE'; - await Promise.resolve(beforeZipCallback(info)); + if (ext === '.jpg' || ext === '.png' || ext === '.jpeg') { + compression = 'STORE'; + } + zip.file(file.dest.replace(/[\\/]/g, '/'), data as Uint8Array, { + compression: compression + }); + }); + promises.push(promise); } - await this.zipPackage(options); - } + await Promise.all(promises); - /** - * Given a root directory, normalize it to a full path. - * Fall back to cwd if not specified - * @param rootDir - */ - public normalizeRootDir(rootDir: string) { - if (!rootDir || (typeof rootDir === 'string' && rootDir.trim().length === 0)) { - return process.cwd(); - } else { - return path.resolve(rootDir); - } + //ensure the outDir exists + await fsExtra.ensureDir( + path.dirname(zipFilePath) + ); + // level 2 compression seems to be the best balance between speed and file size. Speed matters more since most will be calling squashfs afterwards. + const content = await zip.generateAsync({ type: 'nodebuffer', compressionOptions: { level: 2 } }); + return fsExtra.writeFile(zipFilePath, content); } /** @@ -181,7 +124,7 @@ export class RokuDeploy { if (path.isAbsolute(rootDir) === false) { rootDir = this.getOptions({ rootDir: rootDir }).rootDir; } - const entries = this.normalizeFilesArray(files); + const entries = util.normalizeFilesArray(files); const srcPathsByIndex = await util.globAllByIndex( entries.map(x => { return typeof x === 'string' ? x : x.src; @@ -202,7 +145,7 @@ export class RokuDeploy { for (let srcPath of srcPaths) { srcPath = util.standardizePath(srcPath); - const dest = this.computeFileDestPath(srcPath, entry, rootDir); + const dest = util.computeFileDestPath(srcPath, entry, rootDir); //the last file with this `dest` will win, so just replace any existing entry with this one. result.set(dest, { src: srcPath, @@ -214,167 +157,8 @@ export class RokuDeploy { return [...result.values()]; } - /** - * Given a full path to a file, determine its dest path - * @param srcPath the absolute path to the file. This MUST be a file path, and it is not verified to exist on the filesystem - * @param files the files array - * @param rootDir the absolute path to the root dir - * @param skipMatch - skip running the minimatch process (i.e. assume the file is a match - * @returns the RELATIVE path to the dest location for the file. - */ - public getDestPath(srcPathAbsolute: string, files: FileEntry[], rootDir: string, skipMatch = false) { - srcPathAbsolute = util.standardizePath(srcPathAbsolute); - rootDir = rootDir.replace(/\\+/g, '/'); - const entries = this.normalizeFilesArray(files); - - function makeGlobAbsolute(pattern: string) { - return path.resolve( - rootDir, - //remove leading exclamation point if pattern is negated - pattern - //coerce all slashes to forward - ).replace(/\\+/g, '/'); - } - - let result: string; - - //add the file into every matching cache bucket - for (let entry of entries) { - const pattern = (typeof entry === 'string' ? entry : entry.src); - //filter previous paths - if (pattern.startsWith('!')) { - const keepFile = picomatch('!' + makeGlobAbsolute(pattern.replace(/^!/, ''))); - if (!keepFile(srcPathAbsolute)) { - result = undefined; - } - } else { - const keepFile = picomatch(makeGlobAbsolute(pattern)); - if (keepFile(srcPathAbsolute)) { - try { - result = this.computeFileDestPath( - srcPathAbsolute, - entry, - util.standardizePath(rootDir) - ); - } catch { - //ignore errors...the file just has no dest path - } - } - } - } - return result; - } - - /** - * Compute the `dest` path. This accounts for magic globstars in the pattern, - * as well as relative paths based on the dest. This is only used internally. - * @param src an absolute, normalized path for a file - * @param dest the `dest` entry for this file. If omitted, files will derive their paths relative to rootDir. - * @param pattern the glob pattern originally used to find this file - * @param rootDir absolute normalized path to the rootDir - */ - private computeFileDestPath(srcPath: string, entry: string | StandardizedFileEntry, rootDir: string) { - let result: string; - let globstarIdx: number; - //files under rootDir with no specified dest - if (typeof entry === 'string') { - if (util.isParentOfPath(rootDir, srcPath, false)) { - //files that are actually relative to rootDir - result = util.stringReplaceInsensitive(srcPath, rootDir, ''); - } else { - // result = util.stringReplaceInsensitive(srcPath, rootDir, ''); - throw new Error('Cannot reference a file outside of rootDir when using a top-level string. Please use a src;des; object instead'); - } - - //non-glob-pattern explicit file reference - } else if (!isGlob(entry.src.replace(/\\/g, '/'), { strict: false })) { - let isEntrySrcAbsolute = path.isAbsolute(entry.src); - let entrySrcPathAbsolute = isEntrySrcAbsolute ? entry.src : util.standardizePath(`${rootDir}/${entry.src}`); - - let isSrcChildOfRootDir = util.isParentOfPath(rootDir, entrySrcPathAbsolute, false); - - let fileNameAndExtension = path.basename(entrySrcPathAbsolute); - - //no dest - if (entry.dest === null || entry.dest === undefined) { - //no dest, absolute path or file outside of rootDir - if (isEntrySrcAbsolute || isSrcChildOfRootDir === false) { - //copy file to root of staging folder - result = fileNameAndExtension; - - //no dest, relative path, lives INSIDE rootDir - } else { - //copy relative file structure to root of staging folder - let srcPathRelative = util.stringReplaceInsensitive(entrySrcPathAbsolute, rootDir, ''); - result = srcPathRelative; - } - - //assume entry.dest is the relative path to the folder AND file if applicable - } else if (entry.dest === '') { - result = fileNameAndExtension; - } else { - result = entry.dest; - } - //has a globstar - } else if ((globstarIdx = entry.src.indexOf('**')) > -1) { - const rootGlobstarPath = path.resolve(rootDir, entry.src.substring(0, globstarIdx)) + path.sep; - const srcPathRelative = util.stringReplaceInsensitive(srcPath, rootGlobstarPath, ''); - if (entry.dest) { - result = `${entry.dest}/${srcPathRelative}`; - } else { - result = srcPathRelative; - } - - //`pattern` is some other glob magic - } else { - const fileNameAndExtension = path.basename(srcPath); - result = util.standardizePath(`${entry.dest ?? ''}/${fileNameAndExtension}`); - } - - result = util.standardizePath( - //remove leading slashes - result.replace(/^[\/\\]+/, '') - ); - return result; - } - - /** - * Copy all of the files to the staging directory - * @param fileGlobs - * @param stagingPath - */ - private async copyToStaging(files: FileEntry[], stagingPath: string, rootDir: string) { - if (!stagingPath) { - throw new Error('stagingPath is required'); - } - if (!rootDir) { - throw new Error('rootDir is required'); - } - if (!await this.fsExtra.pathExists(rootDir)) { - throw new Error(`rootDir does not exist at "${rootDir}"`); - } - - let fileObjects = await this.getFilePaths(files, rootDir); - //copy all of the files - await Promise.all(fileObjects.map(async (fileObject) => { - let destFilePath = util.standardizePath(`${stagingPath}/${fileObject.dest}`); - - //make sure the containing folder exists - await this.fsExtra.ensureDir(path.dirname(destFilePath)); - - //sometimes the copyfile action fails due to race conditions (normally to poorly constructed src;dest; objects with duplicate files in them - await util.tryRepeatAsync(async () => { - //copy the src item using the filesystem - await this.fsExtra.copy(fileObject.src, destFilePath, { - //copy the actual files that symlinks point to, not the symlinks themselves - dereference: true - }); - }, 10); - })); - } - - private generateBaseRequestOptions(requestPath: string, options: RokuDeployOptions, formData = {} as T): requestType.OptionsWithUrl { - options = this.getOptions(options); + private generateBaseRequestOptions(requestPath: string, options: BaseRequestOptions, formData = {} as T): requestType.OptionsWithUrl { + options = this.getOptions(options) as any; let url = `http://${options.host}:${options.packagePort}/${requestPath}`; let baseRequestOptions = { url: url, @@ -390,50 +174,96 @@ export class RokuDeploy { return baseRequestOptions; } + public async keyPress(options: KeyPressOptions) { + return this.sendKeyEvent({ + ...options, + key: options.key, + action: 'keypress' + }); + } + + public async keyUp(options: KeyUpOptions) { + return this.sendKeyEvent({ + ...options, + action: 'keyup' + }); + } + + public async keyDown(options: KeyDownOptions) { + return this.sendKeyEvent({ + ...options, + action: 'keydown' + }); + } + + public async sendText(options: SendTextOptions) { + const chars = options.text.split(''); + for (const char of chars) { + await this.sendKeyEvent({ + ...options, + key: `lit_${encodeURIComponent(char)}`, + action: 'keypress' + }); + } + } + /** * Simulate pressing the home button on the remote for this roku. * This makes the roku return to the home screen - * @param host - the host - * @param port - the port that should be used for the request. defaults to 8060 - * @param timeout - request timeout duration in milliseconds. defaults to 150000 */ - public async pressHomeButton(host, port?: number, timeout?: number) { - let options = this.getOptions(); - port = port ? port : options.remotePort; - timeout = timeout ? timeout : options.timeout; + private async sendKeyEvent(options: SendKeyEventOptions) { + logger.info('Sending key event:', options.key); + this.checkRequiredOptions(options, ['host', 'key']); + let filledOptions = this.getOptions(options); // press the home button to return to the main screen return this.doPostRequest({ - url: `http://${host}:${port}/keypress/Home`, - timeout: timeout + url: `http://${filledOptions.host}:${filledOptions.remotePort}/${filledOptions.action}/${filledOptions.key}`, + timeout: filledOptions.timeout }, false); } + public async closeChannel(options: CloseChannelOptions) { + // TODO: After 13.0 releases, add check for ECP close-app support, and use that twice to kill instant resume if available + await this.sendKeyEvent({ + ...options, + action: 'keypress', + key: 'home' + }); + } + /** * Publish a pre-existing packaged zip file to a remote Roku. * @param options */ - public async publish(options: RokuDeployOptions): Promise<{ message: string; results: any }> { - options = this.getOptions(options); - if (!options.host) { - throw new errors.MissingRequiredOptionError('must specify the host for the Roku device'); - } + public async sideload(options: SideloadOptions): Promise<{ message: string; results: any }> { + logger.info('Beggining to sideload package'); + this.checkRequiredOptions(options, ['host', 'password']); + options = this.getOptions(options) as any; //make sure the outDir exists - await this.fsExtra.ensureDir(options.outDir); + await fsExtra.ensureDir(options.outDir); + + let zipFilePath = this.getOutputZipFilePath(options as any); - let zipFilePath = this.getOutputZipFilePath(options); - let readStream: _fsExtra.ReadStream; + if (options.deleteDevChannel) { + try { + await this.deleteDevChannel(options); + } catch (e) { + // note we don't report the error; as we don't actually care that we could not deploy - it's just useless noise to log it. + } + } + + let readStream: ReadStream; try { - if ((await this.fsExtra.pathExists(zipFilePath)) === false) { - throw new Error(`Cannot publish because file does not exist at '${zipFilePath}'`); + if ((await fsExtra.pathExists(zipFilePath)) === false) { + throw new Error(`Cannot sideload because file does not exist at '${zipFilePath}'`); } - readStream = this.fsExtra.createReadStream(zipFilePath); + readStream = fsExtra.createReadStream(zipFilePath); //wait for the stream to open (no harm in doing this, and it helps solve an issue in the tests) await new Promise((resolve) => { readStream.on('open', resolve); }); const route = options.packageUploadOverrides?.route ?? 'plugin_install'; - let requestOptions = this.generateBaseRequestOptions(route, options, { mysubmit: 'Replace', archive: readStream, @@ -465,6 +295,7 @@ export class RokuDeploy { let response: HttpResponse; try { try { + console.log('calling once'); response = await this.doPostRequest(requestOptions); } catch (replaceError: any) { //fail if this is a compile error @@ -504,17 +335,18 @@ export class RokuDeploy { if (response.body.indexOf('Identical to previous version -- not replacing.') > -1) { return { message: 'Identical to previous version -- not replacing', results: response }; } - return { message: 'Successful deploy', results: response }; + logger.info('Successful sideload'); + return { message: 'Successful sideload', results: response }; } finally { //delete the zip file only if configured to do so if (options.retainDeploymentArchive === false) { - await this.fsExtra.remove(zipFilePath); + await fsExtra.remove(zipFilePath); } //try to close the read stream to prevent files becoming locked try { readStream?.close(); } catch (e) { - this.logger.info('Error closing read stream', e); + logger.warn('Error closing read stream', e); } } } @@ -541,15 +373,13 @@ export class RokuDeploy { } /** - * Converts existing loaded package to squashfs for faster loading packages + * Converts the currently sideloaded dev app to squashfs for faster loading packages * @param options */ - public async convertToSquashfs(options: RokuDeployOptions) { - options = this.getOptions(options); - if (!options.host) { - throw new errors.MissingRequiredOptionError('must specify the host for the Roku device'); - } - let requestOptions = this.generateBaseRequestOptions('plugin_install', options, { + public async convertToSquashfs(options: ConvertToSquashfsOptions) { + this.checkRequiredOptions(options, ['host', 'password']); + options = this.getOptions(options) as any; + let requestOptions = this.generateBaseRequestOptions('plugin_install', options as any, { archive: '', mysubmit: 'Convert to squashfs' }); @@ -568,6 +398,7 @@ export class RokuDeploy { return results; } } catch (e) { + logger.warn('Error converting to squashfs:', error); throw error; } } else { @@ -583,29 +414,23 @@ export class RokuDeploy { * resign Roku Device with supplied pkg and * @param options */ - public async rekeyDevice(options: RokuDeployOptions) { - options = this.getOptions(options); - if (!options.rekeySignedPackage) { - throw new errors.MissingRequiredOptionError('Must supply rekeySignedPackage'); - } - - if (!options.signingPassword) { - throw new errors.MissingRequiredOptionError('Must supply signingPassword'); - } + public async rekeyDevice(options: RekeyDeviceOptions) { + this.checkRequiredOptions(options, ['host', 'password', 'rekeySignedPackage', 'signingPassword']); + options = this.getOptions(options) as any; let rekeySignedPackagePath = options.rekeySignedPackage; if (!path.isAbsolute(options.rekeySignedPackage)) { rekeySignedPackagePath = path.join(options.rootDir, options.rekeySignedPackage); } - let requestOptions = this.generateBaseRequestOptions('plugin_inspect', options, { + let requestOptions = this.generateBaseRequestOptions('plugin_inspect', options as any, { mysubmit: 'Rekey', passwd: options.signingPassword, - archive: null as _fsExtra.ReadStream + archive: null as ReadStream }); let results: HttpResponse; try { - requestOptions.formData.archive = this.fsExtra.createReadStream(rekeySignedPackagePath); + requestOptions.formData.archive = fsExtra.createReadStream(rekeySignedPackagePath); results = await this.doPostRequest(requestOptions); } finally { //ensure the stream is closed @@ -636,16 +461,42 @@ export class RokuDeploy { * Sign a pre-existing package using Roku and return path to retrieve it * @param options */ - public async signExistingPackage(options: RokuDeployOptions): Promise { - options = this.getOptions(options); - if (!options.signingPassword) { - throw new errors.MissingRequiredOptionError('Must supply signingPassword'); + public async createSignedPackage(options: CreateSignedPackageOptions): Promise { + logger.info('Creating signed package'); + this.checkRequiredOptions(options, ['host', 'password', 'signingPassword']); + options = this.getOptions(options) as any; + + // Process options for app title and app version + if (options.appTitle || options.appVersion) { + if (!options.appTitle || !options.appVersion) { + throw new Error('Either appTitle and appVersion is missing; both must be provided, or a manifestPath can be provided instead.'); + } + } else if (options.manifestPath) { + let manifestPath = path.resolve(options.cwd, options.manifestPath); + let parsedManifest = await this.parseManifest(manifestPath); + if (parsedManifest.major_version === undefined || parsedManifest.minor_version === undefined) { + throw new Error('Either major or minor version is missing from the manifest'); + } + options.appVersion = parsedManifest.major_version + '.' + parsedManifest.minor_version; + options.appTitle = parsedManifest.title; + if (!options.appTitle) { + throw new Error('Value for appTitle is missing from the manifest'); + } + } else { + throw new Error('Either appTitle and appVersion or manifestPath must be provided'); } - let manifestPath = path.join(options.stagingDir, 'manifest'); - let parsedManifest = await this.parseManifest(manifestPath); - let appName = parsedManifest.title + '/' + parsedManifest.major_version + '.' + parsedManifest.minor_version; - let requestOptions = this.generateBaseRequestOptions('plugin_package', options, { + let appName = options.appTitle + '/' + options.appVersion; + + //prevent devId mismatch (if devId is specified) + if (options.devId) { + const deviceDevId = await this.getDevId(options); + if (options.devId !== deviceDevId) { + throw new Error(`Package signing cancelled: provided devId '${options.devId}' does not match on-device devId '${deviceDevId}'`); + } + } + + let requestOptions = this.generateBaseRequestOptions('plugin_package', options as any, { mysubmit: 'Package', pkg_time: (new Date()).getTime(), //eslint-disable-line camelcase passwd: options.signingPassword, @@ -667,25 +518,18 @@ export class RokuDeploy { //for some reason we couldn't find the pkgPath from json, look in the tag pkgSearchMatches = //.exec(results.body); if (pkgSearchMatches) { - return pkgSearchMatches[1]; + const url = pkgSearchMatches[1]; + let requestOptions2 = this.generateBaseRequestOptions(url, options); + + let pkgFilePath = this.getOutputPkgFilePath(options as any); + await this.downloadFile(requestOptions2, pkgFilePath); + logger.info('Signed package created at:', pkgFilePath); + return pkgFilePath; } throw new errors.UnknownDeviceResponseError('Unknown error signing package', results); } - /** - * Sign a pre-existing package using Roku and return path to retrieve it - * @param pkgPath - * @param options - */ - public async retrieveSignedPackage(pkgPath: string, options: RokuDeployOptions): Promise { - options = this.getOptions(options); - let requestOptions = this.generateBaseRequestOptions(pkgPath, options); - - let pkgFilePath = this.getOutputPkgFilePath(options); - return this.getToFile(requestOptions, pkgFilePath); - } - /** * Set the `User-Agent` header if missing from the request params, ensuring it's included in all requests made by roku-deploy * @param params @@ -711,7 +555,7 @@ export class RokuDeploy { private getUserAgent() { try { if (this._packageVersion === undefined) { - this._packageVersion = _fsExtra.readJsonSync(`${__dirname}/../package.json`).version; + this._packageVersion = fsExtra.readJsonSync(`${__dirname}/../package.json`).version; } } catch (e) { this._packageVersion = null; @@ -726,6 +570,7 @@ export class RokuDeploy { * @param params */ private async doPostRequest(params: requestType.OptionsWithUrl, verify = true) { + logger.info('handling POST request to', params.url); let results: { response: any; body: any } = await new Promise((resolve, reject) => { this.setUserAgentIfMissing(params); @@ -748,6 +593,7 @@ export class RokuDeploy { * @param params */ private async doGetRequest(params: requestType.OptionsWithUrl) { + logger.info('handling GET request to', params.url); let results: { response: any; body: any } = await new Promise((resolve, reject) => { this.setUserAgentIfMissing(params); @@ -893,32 +739,16 @@ export class RokuDeploy { return []; } - /** - * Create a zip of the project, and then publish to the target Roku device - * @param options - */ - public async deploy(options?: RokuDeployOptions, beforeZipCallback?: (info: BeforeZipCallbackInfo) => void) { - options = this.getOptions(options); - await this.createPackage(options, beforeZipCallback); - if (options.deleteInstalledChannel) { - try { - await this.deleteInstalledChannel(options); - } catch (e) { - // note we don't report the error; as we don't actually care that we could not deploy - it's just useless noise to log it. - } - } - let result = await this.publish(options); - return result; - } - /** * Deletes any installed dev channel on the target Roku device * @param options */ - public async deleteInstalledChannel(options?: RokuDeployOptions) { - options = this.getOptions(options); + public async deleteDevChannel(options?: DeleteDevChannelOptions) { + logger.info('Deleting dev channel...'); + this.checkRequiredOptions(options, ['host', 'password']); + options = this.getOptions(options) as any; - let deleteOptions = this.generateBaseRequestOptions('plugin_install', options); + let deleteOptions = this.generateBaseRequestOptions('plugin_install', options as any); deleteOptions.formData = { mysubmit: 'Delete', archive: '' @@ -976,14 +806,15 @@ export class RokuDeploy { /** * Gets a screenshot from the device. A side-loaded channel must be running or an error will be thrown. */ - public async takeScreenshot(options: TakeScreenshotOptions) { - options.outDir = options.outDir ?? this.screenshotDir; - options.outFile = options.outFile ?? `screenshot-${dayjs().format('YYYY-MM-DD-HH.mm.ss.SSS')}`; + public async captureScreenshot(options: CaptureScreenshotOptions) { + this.checkRequiredOptions(options, ['host', 'password']); + options = this.getOptions(options); + options.screenshotFile ??= `screenshot-${dayjs().format('YYYY-MM-DD-HH.mm.ss.SSS')}`; let saveFilePath: string; // Ask for the device to make an image let createScreenshotResult = await this.doPostRequest({ - ...this.generateBaseRequestOptions('plugin_inspect', options), + ...this.generateBaseRequestOptions('plugin_inspect', options as any), formData: { mysubmit: 'Screenshot', archive: '' @@ -994,22 +825,22 @@ export class RokuDeploy { const [_, imageUrlOnDevice, imageExt] = /["'](pkgs\/dev(\.jpg|\.png)\?.+?)['"]/gi.exec(createScreenshotResult.body) ?? []; if (imageUrlOnDevice) { - saveFilePath = util.standardizePath(path.join(options.outDir, options.outFile + imageExt)); - await this.getToFile( + saveFilePath = util.standardizePath(path.join(options.screenshotDir, options.screenshotFile + imageExt)); + await this.downloadFile( this.generateBaseRequestOptions(imageUrlOnDevice, options), saveFilePath ); } else { - throw new Error('No screen shot url returned from device'); + throw new Error('No screenshot url returned from device'); } return saveFilePath; } - private async getToFile(requestParams: any, filePath: string) { - let writeStream: _fsExtra.WriteStream; - await this.fsExtra.ensureFile(filePath); + private async downloadFile(requestParams: any, filePath: string) { + let writeStream: WriteStream; + await fsExtra.ensureFile(filePath); return new Promise((resolve, reject) => { - writeStream = this.fsExtra.createWriteStream(filePath, { + writeStream = fsExtra.createWriteStream(filePath, { flags: 'w' }); if (!writeStream) { @@ -1041,120 +872,71 @@ export class RokuDeploy { } /** - * executes sames steps as deploy and signs the package and stores it in the out folder + * Get an options with all overridden values, and then defaults for missing values * @param options */ - public async deployAndSignPackage(options?: RokuDeployOptions, beforeZipCallback?: (info: BeforeZipCallbackInfo) => void): Promise { - options = this.getOptions(options); - let retainStagingDirInitialValue = options.retainStagingDir; - options.retainStagingDir = true; - await this.deploy(options, beforeZipCallback); - - if (options.convertToSquashfs) { - await this.convertToSquashfs(options); - } - - let remotePkgPath = await this.signExistingPackage(options); - let localPkgFilePath = await this.retrieveSignedPackage(remotePkgPath, options); - if (retainStagingDirInitialValue !== true) { - await this.fsExtra.remove(options.stagingDir); - } - return localPkgFilePath; - } - - /** - * Get an options with all overridden vaues, and then defaults for missing values - * @param options - */ - public getOptions(options: RokuDeployOptions = {}) { - let fileOptions: RokuDeployOptions = {}; - const fileNames = ['rokudeploy.json', 'bsconfig.json']; - if (options.project) { - fileNames.unshift(options.project); - } - - for (const fileName of fileNames) { - if (this.fsExtra.existsSync(fileName)) { - let configFileText = this.fsExtra.readFileSync(fileName).toString(); - let parseErrors = [] as ParseError[]; - fileOptions = parseJsonc(configFileText, parseErrors, { - allowEmptyContent: true, - allowTrailingComma: true, - disallowComments: false - }); - if (parseErrors.length > 0) { - throw new Error(`Error parsing "${path.resolve(fileName)}": ` + JSON.stringify( - parseErrors.map(x => { - return { - message: printParseErrorCode(x.error), - offset: x.offset, - length: x.length - }; - }) - )); - } - break; - } - } - - let defaultOptions = { + public getOptions(options: RokuDeployOptions & T = {} as any): RokuDeployOptions & T { + // Fill in default options for any missing values + options = { + cwd: process.cwd(), outDir: './out', outFile: 'roku-deploy', retainDeploymentArchive: true, - incrementBuildNumber: false, failOnCompileError: true, - deleteInstalledChannel: true, + deleteDevChannel: true, packagePort: 80, remotePort: 8060, timeout: 150000, rootDir: './', files: [...DefaultFiles], username: 'rokudev', - logLevel: LogLevel.log + logLevel: 'error', + screenshotDir: path.join(tempDir, '/roku-deploy/screenshots/'), + ...options }; - - //override the defaults with any found or provided options - let finalOptions = { ...defaultOptions, ...fileOptions, ...options }; - this.logger.logLevel = finalOptions.logLevel; + options.cwd ??= process.cwd(); + logger.logLevel = options.logLevel; //fully resolve the folder paths - finalOptions.rootDir = path.resolve(process.cwd(), finalOptions.rootDir); - finalOptions.outDir = path.resolve(process.cwd(), finalOptions.outDir); - finalOptions.retainStagingDir = (finalOptions.retainStagingDir !== undefined) ? finalOptions.retainStagingDir : finalOptions.retainStagingFolder; - //sync the new option with the old one (for back-compat) - finalOptions.retainStagingFolder = finalOptions.retainStagingDir; - - let stagingDir = finalOptions.stagingDir || finalOptions.stagingFolderPath; + options.rootDir = path.resolve(options.cwd, options.rootDir); + options.outDir = path.resolve(options.cwd, options.outDir); + options.screenshotDir = path.resolve(options.cwd, options.screenshotDir); //stagingDir - if (stagingDir) { - finalOptions.stagingDir = path.resolve(process.cwd(), stagingDir); + if (options.stagingDir) { + options.stagingDir = path.resolve(options.cwd, options.stagingDir); } else { - finalOptions.stagingDir = path.resolve( - process.cwd(), - util.standardizePath(`${finalOptions.outDir}/.roku-deploy-staging`) + options.stagingDir = path.resolve( + options.cwd, + util.standardizePath(`${options.outDir}/.roku-deploy-staging`) ); } - //sync the new option with the old one (for back-compat) - finalOptions.stagingFolderPath = finalOptions.stagingDir; - return finalOptions; + logger.info('Retrieved options:', options); + return options; + } + + public checkRequiredOptions>(options: T, requiredOptions: Array) { + for (let opt of requiredOptions as string[]) { + if (options[opt] === undefined) { + throw new Error('Missing required option: ' + opt); + } + } } /** * Centralizes getting output zip file path based on passed in options * @param options */ - public getOutputZipFilePath(options: RokuDeployOptions) { - options = this.getOptions(options); + private getOutputZipFilePath(options?: GetOutputZipFilePathOptions) { + options = this.getOptions(options) as any; let zipFileName = options.outFile; if (!zipFileName.toLowerCase().endsWith('.zip') && !zipFileName.toLowerCase().endsWith('.squashfs')) { zipFileName += '.zip'; } - let outFolderPath = path.resolve(options.outDir); - - let outZipFilePath = path.join(outFolderPath, zipFileName); + let outZipFilePath = path.resolve(options.cwd, options.outDir, zipFileName); + logger.debug('Output zip file path:', outZipFilePath); return outZipFilePath; } @@ -1162,8 +944,8 @@ export class RokuDeploy { * Centralizes getting output pkg file path based on passed in options * @param options */ - public getOutputPkgFilePath(options?: RokuDeployOptions) { - options = this.getOptions(options); + private getOutputPkgFilePath(options?: GetOutputPkgFilePathOptions) { + options = this.getOptions(options) as any; let pkgFileName = options.outFile; if (pkgFileName.toLowerCase().endsWith('.zip')) { @@ -1174,6 +956,7 @@ export class RokuDeploy { let outFolderPath = path.resolve(options.outDir); let outPkgFilePath = path.join(outFolderPath, pkgFileName); + logger.debug('Output pkg file path:', outPkgFilePath); return outPkgFilePath; } @@ -1182,9 +965,10 @@ export class RokuDeploy { * @param host the host or IP address of the Roku * @param port the port to use for the ECP request (defaults to 8060) */ - public async getDeviceInfo(options?: { enhance: true } & GetDeviceInfoOptions): Promise; - public async getDeviceInfo(options?: GetDeviceInfoOptions): Promise + public async getDeviceInfo(options?: GetDeviceInfoOptions & { enhance: true }): Promise; + public async getDeviceInfo(options?: GetDeviceInfoOptions): Promise; public async getDeviceInfo(options: GetDeviceInfoOptions) { + this.checkRequiredOptions(options, ['host']); options = this.getOptions(options) as any; //if the host is a DNS name, look up the IP address @@ -1225,8 +1009,10 @@ export class RokuDeploy { } deviceInfo = result; } + logger.debug('Device info:', deviceInfo); return deviceInfo; } catch (e) { + logger.warn('Error getting device info:', e); throw new errors.UnparsableDeviceResponseError('Could not retrieve device info', response); } } @@ -1260,7 +1046,7 @@ export class RokuDeploy { * decoding HtmlEntities, etc. * @param deviceInfo */ - public normalizeDeviceInfoFieldValue(value: any) { + private normalizeDeviceInfoFieldValue(value: any) { let num: number; // convert 'true' and 'false' string values to boolean if (value === 'true') { @@ -1274,21 +1060,28 @@ export class RokuDeploy { } } - public async getDevId(options?: RokuDeployOptions) { - const deviceInfo = await this.getDeviceInfo(options as any); + /** + * Get the developer ID from the device-info response + * @param options + * @returns + */ + public async getDevId(options?: GetDevIdOptions) { + this.checkRequiredOptions(options, ['host']); + const deviceInfo = await this.getDeviceInfo(options); + logger.debug('Found dev id:', deviceInfo['keyed-developer-id']); return deviceInfo['keyed-developer-id']; } - public async parseManifest(manifestPath: string): Promise { - if (!await this.fsExtra.pathExists(manifestPath)) { + private async parseManifest(manifestPath: string): Promise { + if (!await fsExtra.pathExists(manifestPath)) { throw new Error(manifestPath + ' does not exist'); } - let manifestContents = await this.fsExtra.readFile(manifestPath, 'utf-8'); + let manifestContents = await fsExtra.readFile(manifestPath, 'utf-8'); return this.parseManifestFromString(manifestContents); } - public parseManifestFromString(manifestContents: string): ManifestData { + private parseManifestFromString(manifestContents: string): ManifestData { let manifestLines = manifestContents.split('\n'); let manifestData: ManifestData = {}; manifestData.keyIndexes = {}; @@ -1305,67 +1098,6 @@ export class RokuDeploy { return manifestData; } - public stringifyManifest(manifestData: ManifestData): string { - let output = []; - - if (manifestData.keyIndexes && manifestData.lineCount) { - output.fill('', 0, manifestData.lineCount); - - let key; - for (key in manifestData) { - if (key === 'lineCount' || key === 'keyIndexes') { - continue; - } - - let index = manifestData.keyIndexes[key]; - output[index] = `${key}=${manifestData[key]}`; - } - } else { - output = Object.keys(manifestData).map((key) => { - return `${key}=${manifestData[key]}`; - }); - } - - return output.join('\n'); - } - - /** - * Given a path to a folder, zip up that folder and all of its contents - * @param srcFolder the folder that should be zipped - * @param zipFilePath the path to the zip that will be created - * @param preZipCallback a function to call right before every file gets added to the zip - * @param files a files array used to filter the files from `srcFolder` - */ - public async zipFolder(srcFolder: string, zipFilePath: string, preFileZipCallback?: (file: StandardizedFileEntry, data: Buffer) => Buffer, files: FileEntry[] = ['**/*']) { - const filePaths = await this.getFilePaths(files, srcFolder); - - const zip = new JSZip(); - // Allows us to wait until all are done before we build the zip - const promises = []; - for (const file of filePaths) { - const promise = this.fsExtra.readFile(file.src).then((data) => { - if (preFileZipCallback) { - data = preFileZipCallback(file, data); - } - - const ext = path.extname(file.dest).toLowerCase(); - let compression = 'DEFLATE'; - - if (ext === '.jpg' || ext === '.png' || ext === '.jpeg') { - compression = 'STORE'; - } - zip.file(file.dest.replace(/[\\/]/g, '/'), data as any, { - compression: compression - }); - }); - promises.push(promise); - } - await Promise.all(promises); - // level 2 compression seems to be the best balance between speed and file size. Speed matters more since most will be calling squashfs afterwards. - const content = await zip.generateAsync({ type: 'nodebuffer', compressionOptions: { level: 2 } }); - return this.fsExtra.outputFile(zipFilePath, content); - } - public async rebootDevice(options: RokuDeployOptions) { options = this.getOptions(options); @@ -1379,7 +1111,7 @@ export class RokuDeploy { } return this.doPostRequest({ - ...this.generateBaseRequestOptions('plugin_swup', options), + ...this.generateBaseRequestOptions('plugin_swup', options as any), formData: { mysubmit: 'Reboot', archive: '' @@ -1400,7 +1132,7 @@ export class RokuDeploy { } return this.doPostRequest({ - ...this.generateBaseRequestOptions('plugin_swup', options), + ...this.generateBaseRequestOptions('plugin_swup', options as any), formData: { mysubmit: 'CheckUpdate', archive: '' @@ -1415,21 +1147,6 @@ export interface ManifestData { lineCount?: number; } -export interface BeforeZipCallbackInfo { - /** - * Contains an associative array of the parsed values in the manifest - */ - manifestData: ManifestData; - /** - * @deprecated since 3.9.0. use `stagingDir` instead - */ - stagingFolderPath: string; - /** - * The directory where the files were staged - */ - stagingDir: string; -} - export interface StandardizedFileEntry { /** * The full path to the source file @@ -1468,7 +1185,11 @@ export const DefaultFiles = [ 'source/**/*.*', 'components/**/*.*', 'images/**/*.*', - 'manifest' + 'locale/**/*', + 'fonts/**/*', + 'manifest', + '!node_modules', + '!**/*.{md,DS_Store,db}' ]; export interface HttpResponse { @@ -1476,7 +1197,7 @@ export interface HttpResponse { body: any; } -export interface TakeScreenshotOptions { +export interface CaptureScreenshotOptions { /** * The IP address or hostname of the target Roku device. * @example '192.168.1.21' @@ -1492,13 +1213,18 @@ export interface TakeScreenshotOptions { * A full path to the folder where the screenshots should be saved. * Will use the OS temp directory by default */ - outDir?: string; + screenshotDir?: string; /** * The base filename the image file should be given (excluding the extension) * The default format looks something like this: screenshot-YYYY-MM-DD-HH.mm.ss.SSS. */ - outFile?: string; + screenshotFile?: string; + + /** + * The current working directory to use for relative paths + */ + cwd?: string; } export interface GetDeviceInfoOptions { @@ -1521,4 +1247,156 @@ export interface GetDeviceInfoOptions { enhance?: boolean; } +export type RokuKey = 'back' | 'backspace' | 'channeldown' | 'channelup' | 'down' | 'enter' | 'findremote' | 'fwd' | 'home' | 'info' | 'inputav1' | 'inputhdmi1' | 'inputhdmi2' | 'inputhdmi3' | 'inputhdmi4' | 'inputtuner' | 'instantreplay' | 'left' | 'play' | 'poweroff' | 'rev' | 'right' | 'search' | 'select' | 'up' | 'volumedown' | 'volumemute' | 'volumeup'; + +export interface SendKeyEventOptions { + action?: 'keydown' | 'keypress' | 'keyup'; + host: string; + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents + key: RokuKey | string; + remotePort?: number; + timeout?: number; +} + +export interface KeyUpOptions extends SendKeyEventOptions { + action?: 'keyup'; + key: RokuKey; +} + +export interface KeyDownOptions extends SendKeyEventOptions { + action?: 'keydown'; + key: RokuKey; +} + +export interface KeyPressOptions extends SendKeyEventOptions { + action?: 'keypress'; + key: RokuKey; +} + +export interface SendTextOptions extends SendKeyEventOptions { + action?: 'keypress'; + text: string; +} + +export interface CloseChannelOptions { + host: string; + remotePort?: number; + timeout?: number; + +} +export interface StageOptions { + rootDir?: string; + files?: FileEntry[]; + stagingDir?: string; + cwd?: string; +} + +export interface ZipOptions { + stagingDir?: string; + outDir?: string; + outFile?: string; + cwd?: string; +} + +export interface SideloadOptions { + appType?: 'channel' | 'dcl'; + host: string; + password: string; + remoteDebug?: boolean; + remoteDebugConnectEarly?: boolean; + failOnCompileError?: boolean; + retainDeploymentArchive?: boolean; + outDir?: string; + outFile?: string; + deleteDevChannel?: boolean; + cwd?: string; + packageUploadOverrides?: PackageUploadOverridesOptions; +} + +export interface PackageUploadOverridesOptions { + route?: string; + formData?: Record; +} + +export interface BaseRequestOptions { + host: string; + packagePort?: number; + timeout?: number; + username?: string; + password: string; +} + +export interface ConvertToSquashfsOptions { + host: string; + password: string; +} + +export interface RekeyDeviceOptions { + host: string; + password: string; + rekeySignedPackage: string; + signingPassword: string; + rootDir?: string; + devId: string; + cwd?: string; +} + +export interface CreateSignedPackageOptions { + host: string; + password: string; + signingPassword: string; + appTitle?: string; + appVersion?: string; + manifestPath?: string; + outDir?: string; + /** + * If specified, signing will fail if the device's devId is different than this value + */ + devId?: string; + cwd?: string; +} + +export interface DeleteDevChannelOptions { + host: string; + password: string; +} + +export interface GetOutputZipFilePathOptions { + outFile?: string; + outDir?: string; + cwd?: string; +} + +export interface DeployOptions { + host: string; + password: string; + files?: FileEntry[]; + rootDir?: string; + stagingDir?: string; + deleteDevChannel?: boolean; + outFile?: string; + outDir?: string; + cwd?: string; +} + +export interface GetOutputPkgFilePathOptions { + outFile?: string; + outDir?: string; + cwd?: string; +} + +export interface GetDevIdOptions { + host: string; + /** + * The port to use to send the device-info request (defaults to the standard 8060 ECP port) + */ + remotePort?: number; + /** + * The number of milliseconds at which point this request should timeout and return a rejected promise + */ + timeout?: number; +} + +//create a new static instance of RokuDeploy, and export those functions for backwards compatibility +export const rokuDeploy = new RokuDeploy(); export type EcpNetworkAccessMode = 'enabled' | 'disabled' | 'limited' | 'permissive'; diff --git a/src/RokuDeployOptions.ts b/src/RokuDeployOptions.ts index a6fc07f..8879ad3 100644 --- a/src/RokuDeployOptions.ts +++ b/src/RokuDeployOptions.ts @@ -1,10 +1,10 @@ -import type { LogLevel } from './Logger'; +import type { LogLevel } from '@rokucommunity/logger'; export interface RokuDeployOptions { /** - * Path to a bsconfig.json project file + * The working directory where the command should be executed */ - project?: string; + cwd?: string; /** * A full path to the folder where the zip/pkg package should be placed @@ -43,31 +43,12 @@ export interface RokuDeployOptions { */ files?: FileEntry[]; - /** - * Set this to true to prevent the staging folder from being deleted after creating the package - * @default false - * @deprecated use `retainStagingDir` instead - */ - retainStagingFolder?: boolean; - - /** - * Set this to true to prevent the staging folder from being deleted after creating the package - * @default false - */ - retainStagingDir?: boolean; - /** * Should the zipped package be retained after deploying to a roku. If false, this will delete the zip after a deployment. * @default true */ retainDeploymentArchive?: boolean; - /** - * The path where roku-deploy should stage all of the files right before being zipped. defaults to ${outDir}/.roku-deploy-staging - * @deprecated since 3.9.0. use `stagingDir` instead - */ - stagingFolderPath?: string; - /** * The path where roku-deploy should stage all of the files right before being zipped. defaults to ${outDir}/.roku-deploy-staging */ @@ -105,6 +86,11 @@ export interface RokuDeployOptions { */ remotePort?: number; + /** + * The directory where screenshots should be saved. Will use the OS temp directory by default + */ + screenshotDir?: string; + /** * The request timeout duration in milliseconds. Defaults to 150000ms (2 minutes 30 seconds). * This is mainly useful for preventing hang ups if the Roku loses power or restarts due to a firmware bug. @@ -139,16 +125,6 @@ export interface RokuDeployOptions { */ devId?: string; - /** - * If true we increment the build number to be a timestamp in the format yymmddHHMM - */ - incrementBuildNumber?: boolean; - - /** - * If true we convert to squashfs before creating the pkg file - */ - convertToSquashfs?: boolean; - /** * If true, the publish will fail on compile error */ @@ -163,7 +139,7 @@ export interface RokuDeployOptions { /** * If true, the previously installed dev channel will be deleted before installing the new one */ - deleteInstalledChannel?: boolean; + deleteDevChannel?: boolean; /** * Overrides for values used during the zip upload process. You probably don't need to change these... @@ -182,4 +158,4 @@ export interface RokuDeployOptions { }; } -export type FileEntry = (string | { src: string | string[]; dest?: string }); +export type FileEntry = (string | { src: string[] | string; dest?: string }); diff --git a/src/cli.spec.ts b/src/cli.spec.ts new file mode 100644 index 0000000..d5536b2 --- /dev/null +++ b/src/cli.spec.ts @@ -0,0 +1,343 @@ +import * as childProcess from 'child_process'; +import { cwd, expectPathExists, rootDir, stagingDir, tempDir, outDir } from './testUtils.spec'; +import * as fsExtra from 'fs-extra'; +import { expect } from 'chai'; +import { createSandbox } from 'sinon'; +import { rokuDeploy } from './index'; +import { SideloadCommand } from './commands/SideloadCommand'; +import { ConvertToSquashfsCommand } from './commands/ConvertToSquashfsCommand'; +import { RekeyDeviceCommand } from './commands/RekeyDeviceCommand'; +import { CreateSignedPackageCommand } from './commands/CreateSignedPackageCommand'; +import { DeleteDevChannelCommand } from './commands/DeleteDevChannelCommand'; +import { CaptureScreenshotCommand } from './commands/CaptureScreenshotCommand'; +import { GetDeviceInfoCommand } from './commands/GetDeviceInfoCommand'; +import { GetDevIdCommand } from './commands/GetDevIdCommand'; + +const sinon = createSandbox(); + +function execSync(command: string) { + const output = childProcess.execSync(command, { cwd: tempDir }); + process.stdout.write(output); + return output; +} +describe('cli', () => { + before(function build() { + this.timeout(20000); + execSync('npm run build'); + }); + beforeEach(() => { + fsExtra.emptyDirSync(tempDir); + //most tests depend on a manifest file existing, so write an empty one + fsExtra.outputFileSync(`${rootDir}/manifest`, ''); + sinon.restore(); + }); + afterEach(() => { + fsExtra.removeSync(tempDir); + sinon.restore(); + }); + + it('Successfully runs stage', () => { + //make the files + fsExtra.outputFileSync(`${rootDir}/source/main.brs`, ''); + + expect(() => { + execSync(`node ${cwd}/dist/cli.js stage --stagingDir ${stagingDir} --rootDir ${rootDir}`); + }).to.not.throw(); + }); + + it('Successfully copies rootDir folder to staging folder', () => { + fsExtra.outputFileSync(`${rootDir}/source/main.brs`, ''); + + execSync(`node ${cwd}/dist/cli.js stage --rootDir ${rootDir} --stagingDir ${stagingDir}`); + + expectPathExists(`${stagingDir}/source/main.brs`); + }); + + it('SideloadCommand passes proper options when zip is provided', async () => { + sinon.stub(rokuDeploy, 'closeChannel').callsFake(async () => { + return Promise.resolve(); + }); + const sideloadStub = sinon.stub(rokuDeploy, 'sideload').callsFake(async () => { + return Promise.resolve({ + message: 'Successful sideload', + results: {} + }); + }); + + const command = new SideloadCommand(); + await command.run({ + host: '1.2.3.4', + password: '5536', + zip: 'test.zip' + }); + + expect( + sideloadStub.getCall(0).args[0] + ).to.eql({ + host: '1.2.3.4', + password: '5536', + outDir: cwd, + outFile: 'test.zip', + zip: 'test.zip', + retainDeploymentArchive: true + }); + }); + + it('SideloadCommand passes proper options when rootDir is provided', async () => { + sinon.stub(rokuDeploy, 'closeChannel').callsFake(async () => { + return Promise.resolve(); + }); + sinon.stub(rokuDeploy, 'zip').callsFake(async () => { + return Promise.resolve(); + }); + const sideloadStub = sinon.stub(rokuDeploy, 'sideload').callsFake(async () => { + return Promise.resolve({ + message: 'Successful sideload', + results: {} + }); + }); + + const command = new SideloadCommand(); + await command.run({ + host: '1.2.3.4', + password: '5536', + rootDir: rootDir + }); + + expect( + sideloadStub.getCall(0).args[0] + ).to.eql({ + host: '1.2.3.4', + password: '5536', + rootDir: rootDir, + retainDeploymentArchive: false + }); + }); + + it('SideloadCommand throws error when neither zip nor rootDir is provided', async () => { + const command = new SideloadCommand(); + + try { + await command.run({ + host: '1.2.3.4', + password: '5536', + noclose: true + }); + expect.fail('Expected an error to be thrown'); + } catch (error) { + expect((error as Error).message).to.equal('Either zip or rootDir must be provided for sideload command'); + } + }); + + it('SideloadCommand calls the proper methods when noclose is provided', async () => { + const closeChannelStub = sinon.stub(rokuDeploy, 'closeChannel').callsFake(async () => { + return Promise.resolve(); + }); + const sideloadStub = sinon.stub(rokuDeploy, 'sideload').callsFake(async () => { + return Promise.resolve({ + message: 'Successful sideload', + results: {} + }); + }); + + const command = new SideloadCommand(); + await command.run({ + host: '1.2.3.4', + password: '5536', + zip: 'test.zip', + noclose: true + }); + + expect(closeChannelStub.callCount).to.equal(0); + + expect( + sideloadStub.getCall(0).args[0] + ).to.eql({ + host: '1.2.3.4', + password: '5536', + noclose: true, + outDir: cwd, + outFile: 'test.zip', + retainDeploymentArchive: true, + zip: 'test.zip' + }); + }); + + it('Converts to squashfs', async () => { + const stub = sinon.stub(rokuDeploy, 'convertToSquashfs').callsFake(async () => { + return Promise.resolve(); + }); + + const command = new ConvertToSquashfsCommand(); + await command.run({ + host: '1.2.3.4', + password: '5536' + }); + + expect( + stub.getCall(0).args[0] + ).to.eql({ + host: '1.2.3.4', + password: '5536' + }); + }); + + it('Rekeys a device', async () => { + const stub = sinon.stub(rokuDeploy, 'rekeyDevice').callsFake(async () => { + return Promise.resolve(); + }); + + const command = new RekeyDeviceCommand(); + await command.run({ + host: '1.2.3.4', + password: '5536', + rekeySignedPackage: `${tempDir}/testSignedPackage.pkg`, + signingPassword: '12345', + rootDir: rootDir, + devId: 'abcde' + }); + + expect( + stub.getCall(0).args[0] + ).to.eql({ + host: '1.2.3.4', + password: '5536', + rekeySignedPackage: `${tempDir}/testSignedPackage.pkg`, + signingPassword: '12345', + rootDir: rootDir, + devId: 'abcde' + }); + }); + + it('Signs an existing package', async () => { + const stub = sinon.stub(rokuDeploy, 'createSignedPackage').callsFake(async () => { + return Promise.resolve(''); + }); + + const command = new CreateSignedPackageCommand(); + await command.run({ + host: '1.2.3.4', + password: '5536', + signingPassword: undefined, + stagingDir: stagingDir + }); + + expect( + stub.getCall(0).args[0] + ).to.eql({ + host: '1.2.3.4', + password: '5536', + signingPassword: undefined, + stagingDir: stagingDir + }); + }); + + it('Deletes an installed channel', async () => { + const stub = sinon.stub(rokuDeploy, 'deleteDevChannel').callsFake(async () => { + return Promise.resolve({ response: {}, body: {} }); + }); + + const command = new DeleteDevChannelCommand(); + await command.run({ + host: '1.2.3.4', + password: '5536' + }); + + expect( + stub.getCall(0).args[0] + ).to.eql({ + host: '1.2.3.4', + password: '5536' + }); + }); + + it('Takes a screenshot', async () => { + const stub = sinon.stub(rokuDeploy, 'captureScreenshot').callsFake(async () => { + return Promise.resolve(''); + }); + + const command = new CaptureScreenshotCommand(); + await command.run({ + host: '1.2.3.4', + password: '5536' + }); + + expect( + stub.getCall(0).args[0] + ).to.eql({ + host: '1.2.3.4', + password: '5536' + }); + }); + + it('Device info arguments are correct', async () => { + const stub = sinon.stub(rokuDeploy, 'getDeviceInfo').callsFake(async () => { + return Promise.resolve({ + response: {}, + body: {} + }); + }); + + const command = new GetDeviceInfoCommand(); + await command.run({ + host: '1.2.3.4' + }); + + expect( + stub.getCall(0).args[0] + ).to.eql({ + host: '1.2.3.4' + }); + }); + + it('Prints device info to console', async () => { + let consoleOutput = ''; + sinon.stub(console, 'log').callsFake((...args) => { + consoleOutput += args.join(' ') + '\n'; + }); + sinon.stub(rokuDeploy, 'getDeviceInfo').returns(Promise.resolve({ + 'device-id': '1234', + 'serial-number': 'abcd' + })); + await new GetDeviceInfoCommand().run({ + host: '1.2.3.4' + }); + + // const consoleOutputObject: Record = { + // 'device-id': '1234', + // 'serial-number': 'abcd' + // }; + + expect(consoleOutput).to.eql([ + 'Name Value ', + '---------------------------', + 'device-id 1234 ', + 'serial-number abcd \n' + ].join('\n')); + }); + + it('Gets dev id', async () => { + const stub = sinon.stub(rokuDeploy, 'getDevId').callsFake(async () => { + return Promise.resolve(''); + }); + + const command = new GetDevIdCommand(); + await command.run({ + host: '1.2.3.4', + password: '5536' + }); + + expect( + stub.getCall(0).args[0] + ).to.eql({ + host: '1.2.3.4', + password: '5536' + }); + }); + + it('Zips a folder', () => { + execSync(`node ${cwd}/dist/cli.js zip --stagingDir ${rootDir} --outDir ${outDir}`); + + expectPathExists(`${outDir}/roku-deploy.zip`); + }); +}); diff --git a/src/cli.ts b/src/cli.ts old mode 100644 new mode 100755 index 063f5fd..e3975ba --- a/src/cli.ts +++ b/src/cli.ts @@ -1,7 +1,213 @@ #!/usr/bin/env node -import { deploy } from './index'; -deploy().then((...args) => { - console.log(...args); -}, (...args) => { - console.error(...args); -}); +import * as yargs from 'yargs'; +import * as path from 'path'; +import { SendTextCommand } from './commands/SendTextCommand'; +import { StageCommand } from './commands/StageCommand'; +import { SideloadCommand } from './commands/SideloadCommand'; +import { ConvertToSquashfsCommand } from './commands/ConvertToSquashfsCommand'; +import { RekeyDeviceCommand } from './commands/RekeyDeviceCommand'; +import { CreateSignedPackageCommand } from './commands/CreateSignedPackageCommand'; +import { DeleteDevChannelCommand } from './commands/DeleteDevChannelCommand'; +import { CaptureScreenshotCommand } from './commands/CaptureScreenshotCommand'; +import { GetDeviceInfoCommand } from './commands/GetDeviceInfoCommand'; +import { GetDevIdCommand } from './commands/GetDevIdCommand'; +import { ZipCommand } from './commands/ZipCommand'; +import { KeyPressCommand } from './commands/KeyPressCommand'; +import { KeyUpCommand } from './commands/KeyUpCommand'; +import { KeyDownCommand } from './commands/KeyDownCommand'; +import { RemoteControlCommand } from './commands/RemoteControlCommand'; + +void yargs + + .command('sideload', 'Sideload a pre-existing packaged zip file to a remote Roku', (builder) => { + return builder + .option('zip', { type: 'string', description: 'The file to be sideloaded (instead of a folder), relative to cwd.', demandOption: false }) + .option('rootDir', { type: 'string', description: 'The root folder to be sideloaded (instead of a zip file), relative to cwd.', demandOption: false }) + .option('outZip', { type: 'string', description: 'The output path to the zip file.', demandOption: false }) + .option('host', { type: 'string', description: 'The IP Address of the target Roku', demandOption: false }) + .option('password', { type: 'string', description: 'The password of the target Roku', demandOption: false }) + .option('ecpPort', { type: 'number', description: 'The port to use for ECP commands (like pressing the home button)', demandOption: false }) + .option('packagePort', { type: 'number', description: 'The port to use for sending a packaging to the device', demandOption: false }) + .option('noclose', { type: 'boolean', description: 'Should the command not close the channel before sideloading', demandOption: false }) + .option('timeout', { type: 'number', description: 'The timeout for this command', demandOption: false }) + .option('remoteDebug', { type: 'boolean', description: 'Should the command be run in remote debug mode', demandOption: false }) + .option('remoteDebugConnectEarly', { type: 'boolean', description: 'Should the command connect to the debugger early', demandOption: false }) + .option('failOnCompileError', { type: 'boolean', description: 'Should the command fail if there is a compile error', demandOption: false }) + .option('deleteDevChannel', { type: 'boolean', description: 'Should the dev channel be deleted', demandOption: false }) + .option('cwd', { type: 'string', description: 'The current working directory to use for relative paths', demandOption: false }); + }, (args: any) => { + return new SideloadCommand().run(args); + }) + + .command('package', 'Create a signed package from an existing sideloaded dev channel', (builder) => { + return builder + .option('host', { type: 'string', description: 'The IP Address of the target Roku', demandOption: false }) + .option('password', { type: 'string', description: 'The password of the target Roku', demandOption: false }) + .option('signingPassword', { type: 'string', description: 'The password of the signing key', demandOption: false }) + .option('appTitle', { type: 'string', description: 'The title of the app to be signed', demandOption: false }) + .option('appVersion', { type: 'string', description: 'The version of the app to be signed', demandOption: false }) + .option('manifestPath', { type: 'string', description: 'The path to the manifest file, relative to cwd', demandOption: false }) + .option('out', { type: 'string', description: 'The location where the signed package will be saved, relative to cwd', demandOption: false, defaultDescription: './out/roku-deploy.pkg' }) + .option('devId', { type: 'string', description: 'The dev ID', demandOption: false }) + .option('cwd', { type: 'string', description: 'The current working directory to use for relative paths', demandOption: false }); + }, (args: any) => { + if (args.out) { + if (!args.out.endsWith('.pkg')) { + throw new Error('Out must end with a .pkg'); + } + args.out = path.resolve(args.cwd, args.out); + args.outDir = path.dirname(args.out); + args.outFile = path.basename(args.out); + } + return new CreateSignedPackageCommand().run(args); + }) + + .command('keyPress', 'send keypress command', (builder) => { + return builder + .option('key', { type: 'string', description: 'The key to send', demandOption: true }) + .option('host', { type: 'string', description: 'The IP Address of the target Roku', demandOption: false }) + .option('ecpPort', { type: 'number', description: 'The port to use for ECP commands like remote key presses', demandOption: false }) + .option('timeout', { type: 'number', description: 'The timeout for this command', demandOption: false }); + }, (args: any) => { + if (args.ecpPort) { + args.remotePort = args.ecpPort; + } + return new KeyPressCommand().run(args); + }) + + .command('keyUp', 'send keyup command', (builder) => { + return builder + .option('key', { type: 'string', description: 'The key to send', demandOption: true }) + .option('host', { type: 'string', description: 'The IP Address of the target Roku', demandOption: false }) + .option('ecpPort', { type: 'number', description: 'The port to use for ECP commands like remote key presses', demandOption: false }) + .option('timeout', { type: 'number', description: 'The timeout for this command', demandOption: false }); + }, (args: any) => { + if (args.ecpPort) { + args.remotePort = args.ecpPort; + } + return new KeyUpCommand().run(args); + }) + + .command('keyDown', 'send keydown command', (builder) => { + return builder + .option('key', { type: 'string', description: 'The key to send', demandOption: true }) + .option('host', { type: 'string', description: 'The IP Address of the target Roku', demandOption: false }) + .option('ecpPort', { type: 'number', description: 'The port to use for ECP commands like remote key presses', demandOption: false }) + .option('timeout', { type: 'number', description: 'The timeout for this command', demandOption: false }); + }, (args: any) => { + if (args.ecpPort) { + args.remotePort = args.ecpPort; + } + return new KeyDownCommand().run(args); + }) + + .command('sendText', 'Send text command', (builder) => { + return builder + .option('text', { type: 'string', description: 'The text to send', demandOption: true }) + .option('host', { type: 'string', description: 'The IP Address of the target Roku', demandOption: false }) + .option('ecpPort', { type: 'number', description: 'The port to use for ECP commands like remote key presses', demandOption: false }) + .option('timeout', { type: 'number', description: 'The timeout for this command', demandOption: false }); + }, (args: any) => { + if (args.ecpPort) { + args.remotePort = args.ecpPort; + } + return new SendTextCommand().run(args); + }) + + .command('remote-control', 'Provides a way to send a series of ECP key events similar to how Roku Remote Tool works but from the command line', (builder) => { + return builder + .option('host', { type: 'string', description: 'The IP Address of the target Roku', demandOption: false }) + .option('ecpPort', { type: 'number', description: 'The port to use for ECP commands like remote key presses', demandOption: false }); + }, (args: any) => { + if (args.ecpPort) { + args.remotePort = args.ecpPort; + } + return new RemoteControlCommand().run(args); + }) + + .command('stage', 'Copies all of the referenced files to the staging folder', (builder) => { + return builder + .option('cwd', { type: 'string', description: 'The current working directory to use for relative paths', demandOption: false }) + .option('rootDir', { type: 'string', description: 'The selected root folder to be copied', demandOption: false }) + .option('files', { type: 'array', description: 'An array of source file paths indicating where the source files are', demandOption: false }) + .option('out', { type: 'string', description: 'The selected staging folder where all files will be copied to', demandOption: false }); + }, (args: any) => { + return new StageCommand().run(args); + }) + + .command('squash', 'Convert a pre-existing packaged zip file to a squashfs file', (builder) => { + return builder + .option('host', { type: 'string', description: 'The IP Address of the target Roku', demandOption: false }) + .option('password', { type: 'string', description: 'The password of the target Roku', demandOption: false }); + }, (args: any) => { + return new ConvertToSquashfsCommand().run(args); + }) + + .command('rekey', 'Rekey a device', (builder) => { + return builder + .option('host', { type: 'string', description: 'The IP Address of the target Roku', demandOption: false }) + .option('password', { type: 'string', description: 'The password of the target Roku', demandOption: false }) + .option('pkg', { type: 'string', description: 'The path to the signed package to be used for rekeying, relative to cwd', demandOption: false }) + .option('signingPassword', { type: 'string', description: 'The password of the signing key', demandOption: false }) + .option('devId', { type: 'string', description: 'The dev ID', demandOption: false }) + .option('cwd', { type: 'string', description: 'The current working directory to use for relative paths', demandOption: false }); + }, (args: any) => { + args.rekeySignedPackage = path.resolve(args.cwd, args.pkg); + return new RekeyDeviceCommand().run(args); + }) + + .command('deleteDevChannel', 'Delete an installed channel', (builder) => { + return builder + .option('host', { type: 'string', description: 'The IP Address of the target Roku', demandOption: false }) + .option('password', { type: 'string', description: 'The password of the target Roku', demandOption: false }); + }, (args: any) => { + return new DeleteDevChannelCommand().run(args); + }) + + .command('screenshot', 'Take a screenshot', (builder) => { + return builder + .option('host', { type: 'string', description: 'The IP Address of the target Roku', demandOption: false }) + .option('password', { type: 'string', description: 'The password of the target Roku', demandOption: false }) + .option('out', { type: 'string', description: 'The location where the screenshot will be saved relative to cwd', demandOption: false, defaultDescription: './out/roku-deploy.jpg' }) + .option('cwd', { type: 'string', description: 'The current working directory to use for relative paths', demandOption: false }); + }, (args: any) => { + if (args.out) { + args.out = path.resolve(args.cwd, args.out); + args.screenshotDir = path.dirname(args.out); + args.screenshotFile = path.basename(args.out); + } + return new CaptureScreenshotCommand().run(args); + }) + + .command('getDeviceInfo', 'Get the `device-info` response from a Roku device', (builder) => { + return builder + .option('host', { type: 'string', description: 'The IP Address of the target Roku', demandOption: false }); + }, (args: any) => { + return new GetDeviceInfoCommand().run(args); + }) + + .command('getDevId', 'Get Dev ID', (builder) => { + return builder + .option('host', { type: 'string', description: 'The IP Address of the target Roku', demandOption: false }); + }, (args: any) => { + return new GetDevIdCommand().run(args); + }) + + .command('zip', 'Given a path to a folder, zip up that folder and all of its contents', (builder) => { + return builder + .option('dir', { type: 'string', description: 'The folder to be zipped', demandOption: false }) + .option('out', { type: 'string', description: 'the path to the zip file that will be created, relative to cwd', demandOption: false, alias: 'outZip' }) + .option('cwd', { type: 'string', description: 'The current working directory to use for relative paths', demandOption: false }); + }, (args: any) => { + if (args.out) { + args.out = path.resolve(args.cwd, args.out); + args.outDir = path.dirname(args.out); + args.outFile = path.basename(args.out); + } + if (args.dir) { + args.stagingDir = path.resolve(args.cwd, args.dir); + } + return new ZipCommand().run(args); + }) + + .argv; diff --git a/src/commands/CaptureScreenshotCommand.ts b/src/commands/CaptureScreenshotCommand.ts new file mode 100644 index 0000000..6a75ed5 --- /dev/null +++ b/src/commands/CaptureScreenshotCommand.ts @@ -0,0 +1,11 @@ +import { rokuDeploy, util } from '../index'; + +export class CaptureScreenshotCommand { + async run(args) { + let options = { + ...util.getOptionsFromJson(args), + ...args + }; + await rokuDeploy.captureScreenshot(options); + } +} diff --git a/src/commands/ConvertToSquashfsCommand.ts b/src/commands/ConvertToSquashfsCommand.ts new file mode 100644 index 0000000..79ebf6e --- /dev/null +++ b/src/commands/ConvertToSquashfsCommand.ts @@ -0,0 +1,11 @@ +import { rokuDeploy, util } from '../index'; + +export class ConvertToSquashfsCommand { + async run(args) { + let options = { + ...util.getOptionsFromJson(args), + ...args + }; + await rokuDeploy.convertToSquashfs(options); + } +} diff --git a/src/commands/CreateSignedPackageCommand.ts b/src/commands/CreateSignedPackageCommand.ts new file mode 100644 index 0000000..904a9fe --- /dev/null +++ b/src/commands/CreateSignedPackageCommand.ts @@ -0,0 +1,11 @@ +import { rokuDeploy, util } from '../index'; + +export class CreateSignedPackageCommand { + async run(args) { + let options = { + ...util.getOptionsFromJson(args), + ...args + }; + await rokuDeploy.createSignedPackage(options); + } +} diff --git a/src/commands/DeleteDevChannelCommand.ts b/src/commands/DeleteDevChannelCommand.ts new file mode 100644 index 0000000..5bfa269 --- /dev/null +++ b/src/commands/DeleteDevChannelCommand.ts @@ -0,0 +1,11 @@ +import { rokuDeploy, util } from '../index'; + +export class DeleteDevChannelCommand { + async run(args) { + let options = { + ...util.getOptionsFromJson(args), + ...args + }; + await rokuDeploy.deleteDevChannel(options); + } +} diff --git a/src/commands/GetDevIdCommand.ts b/src/commands/GetDevIdCommand.ts new file mode 100644 index 0000000..c54c8e6 --- /dev/null +++ b/src/commands/GetDevIdCommand.ts @@ -0,0 +1,11 @@ +import { rokuDeploy, util } from '../index'; + +export class GetDevIdCommand { + async run(args) { + let options = { + ...util.getOptionsFromJson(args), + ...args + }; + await rokuDeploy.getDevId(options); + } +} diff --git a/src/commands/GetDeviceInfoCommand.ts b/src/commands/GetDeviceInfoCommand.ts new file mode 100644 index 0000000..3255fe3 --- /dev/null +++ b/src/commands/GetDeviceInfoCommand.ts @@ -0,0 +1,13 @@ +import { rokuDeploy } from '../index'; +import { util } from '../util'; + +export class GetDeviceInfoCommand { + async run(args) { + let options = { + ...util.getOptionsFromJson(args), + ...args + }; + const outputPath = await rokuDeploy.getDeviceInfo(options); + console.log(util.objectToTableString(outputPath)); + } +} diff --git a/src/commands/KeyDownCommand.ts b/src/commands/KeyDownCommand.ts new file mode 100644 index 0000000..ae05b91 --- /dev/null +++ b/src/commands/KeyDownCommand.ts @@ -0,0 +1,11 @@ +import { rokuDeploy, util } from '../index'; + +export class KeyDownCommand { + async run(args) { + let options = { + ...util.getOptionsFromJson(args), + ...args + }; + await rokuDeploy.keyDown(options); + } +} diff --git a/src/commands/KeyPressCommand.ts b/src/commands/KeyPressCommand.ts new file mode 100644 index 0000000..4554df7 --- /dev/null +++ b/src/commands/KeyPressCommand.ts @@ -0,0 +1,11 @@ +import { rokuDeploy, util } from '../index'; + +export class KeyPressCommand { + async run(args) { + let options = { + ...util.getOptionsFromJson(args), + ...args + }; + await rokuDeploy.keyPress(options); + } +} diff --git a/src/commands/KeyUpCommand.ts b/src/commands/KeyUpCommand.ts new file mode 100644 index 0000000..36b71ea --- /dev/null +++ b/src/commands/KeyUpCommand.ts @@ -0,0 +1,11 @@ +import { rokuDeploy, util } from '../index'; + +export class KeyUpCommand { + async run(args) { + let options = { + ...util.getOptionsFromJson(args), + ...args + }; + await rokuDeploy.keyUp(options); + } +} diff --git a/src/commands/RekeyDeviceCommand.ts b/src/commands/RekeyDeviceCommand.ts new file mode 100644 index 0000000..ca00fd8 --- /dev/null +++ b/src/commands/RekeyDeviceCommand.ts @@ -0,0 +1,11 @@ +import { rokuDeploy, util } from '../index'; + +export class RekeyDeviceCommand { + async run(args) { + let options = { + ...util.getOptionsFromJson(args), + ...args + }; + await rokuDeploy.rekeyDevice(options); + } +} diff --git a/src/commands/RemoteControlCommand.ts b/src/commands/RemoteControlCommand.ts new file mode 100644 index 0000000..9ff1f3e --- /dev/null +++ b/src/commands/RemoteControlCommand.ts @@ -0,0 +1,104 @@ +import * as readline from 'readline'; +import type { RokuKey } from '../index'; +import { rokuDeploy, util } from '../index'; + +export class RemoteControlCommand { + run(args) { + let options = { + ...util.getOptionsFromJson(args), + ...args + }; + + if (!options.host) { + console.error('Host is required and neither supplied via --host or in your rokudeploy.json file'); + return; + } + + readline.emitKeypressEvents(process.stdin); + process.stdin.setRawMode(true); + + process.stdin.on('keypress', (str, key) => { + const keyName = key.name as unknown; + let rokuDeployKeyName: RokuKey | undefined; + switch (keyName) { + case 'home': + rokuDeployKeyName = keyName; + break; + case 'escape': + rokuDeployKeyName = 'back'; + break; + case 'delete': + if (key.ctrl || key.meta || key.shift) { + rokuDeployKeyName = 'backspace'; + } + rokuDeployKeyName = 'back'; + break; + case 'backspace': + if (key.ctrl || key.meta || key.shift) { + rokuDeployKeyName = 'backspace'; + } else { + rokuDeployKeyName = 'instantreplay'; + } + break; + case 'end': + rokuDeployKeyName = 'play'; + break; + case 'return': + rokuDeployKeyName = 'select'; + break; + case 'up': + rokuDeployKeyName = 'up'; + if (key.shift) { + rokuDeployKeyName = 'volumeup'; + } + break; + case 'down': + rokuDeployKeyName = 'down'; + if (key.shift) { + rokuDeployKeyName = 'volumedown'; + } + break; + case 'left': + rokuDeployKeyName = 'left'; + if (key.shift) { + rokuDeployKeyName = 'rev'; + } + break; + case 'right': + rokuDeployKeyName = 'right'; + if (key.shift) { + rokuDeployKeyName = 'fwd'; + } + break; + default: + if (key.sequence === '*') { + rokuDeployKeyName = 'info'; + } else { + if (key.ctrl && key.name === 'c') { + process.exit(); // We provide a way to exit the program + } + + let text = key.name; + if (text === undefined) { + text = key.sequence; + } + + if (text === 'space') { + text = ' '; + } + + void rokuDeploy.sendText({ + text: text, ...options + }); + } + break; + } + + if (rokuDeployKeyName) { + void rokuDeploy.keyPress({ key: rokuDeployKeyName, ...options }); + } + }); + + console.log('Now receiving keyboard input. Press Ctrl+C to exit.\nescape=back, end=play, return=select, shift+left=rev, shift+right=fwd, shift+up=volumeup, shift+down=volumedown, *=options'); + } +} diff --git a/src/commands/SendTextCommand.ts b/src/commands/SendTextCommand.ts new file mode 100644 index 0000000..3e11810 --- /dev/null +++ b/src/commands/SendTextCommand.ts @@ -0,0 +1,11 @@ +import { rokuDeploy, util } from '../index'; + +export class SendTextCommand { + async run(args) { + let options = { + ...util.getOptionsFromJson(args), + ...args + }; + await rokuDeploy.sendText(options); + } +} diff --git a/src/commands/SideloadCommand.ts b/src/commands/SideloadCommand.ts new file mode 100644 index 0000000..c488b32 --- /dev/null +++ b/src/commands/SideloadCommand.ts @@ -0,0 +1,47 @@ +import { rokuDeploy, util } from '../index'; +import type { CloseChannelOptions } from '../RokuDeploy'; +import * as path from 'path'; + +export class SideloadCommand { + async run(args) { + let options = { + ...util.getOptionsFromJson(args), + ...args + }; + + // Process args so that they can be compatible with the RokuDeploy + args.cwd ??= process.cwd(); + if (args.zip) { + args.zip = path.resolve(args.cwd, args.zip); + options.outDir = path.dirname(args.zip); + options.outFile = path.basename(args.zip); + } + if (args.rootDir) { + options.rootDir = path.resolve(args.cwd, args.rootDir); + } + + if (args.outZip) { + options.outZip = path.resolve(args.cwd, args.outZip); + } + + if (args.ecpPort) { + options.remotePort = args.ecpPort; + } + + if (args.noclose !== true) { + await rokuDeploy.closeChannel(options as CloseChannelOptions); + } + + + if (args.zip) { + options.retainDeploymentArchive = true; + await rokuDeploy.sideload(options); + } else if (args.rootDir) { + await rokuDeploy.zip(options); + options.retainDeploymentArchive = false; + await rokuDeploy.sideload(options); + } else { + throw new Error('Either zip or rootDir must be provided for sideload command'); + } + } +} diff --git a/src/commands/StageCommand.ts b/src/commands/StageCommand.ts new file mode 100644 index 0000000..8cdf460 --- /dev/null +++ b/src/commands/StageCommand.ts @@ -0,0 +1,11 @@ +import { rokuDeploy, util } from '../index'; + +export class StageCommand { + async run(args) { + let options = { + ...util.getOptionsFromJson(args), + ...args + }; + await rokuDeploy.stage(options); + } +} diff --git a/src/commands/ZipCommand.ts b/src/commands/ZipCommand.ts new file mode 100644 index 0000000..b03e80d --- /dev/null +++ b/src/commands/ZipCommand.ts @@ -0,0 +1,11 @@ +import { rokuDeploy, util } from '../index'; + +export class ZipCommand { + async run(args) { + let options = { + ...util.getOptionsFromJson(args), + ...args + }; + await rokuDeploy.zip(options); + } +} diff --git a/src/device.spec.ts b/src/device.spec.ts index adfa994..98fdd13 100644 --- a/src/device.spec.ts +++ b/src/device.spec.ts @@ -1,26 +1,14 @@ -import * as assert from 'assert'; import * as fsExtra from 'fs-extra'; -import * as rokuDeploy from './index'; -import { cwd, expectPathExists, expectThrowsAsync, outDir, rootDir, tempDir, writeFiles } from './testUtils.spec'; +import { cwd, rootDir, tempDir, writeFiles } from './testUtils.spec'; import undent from 'undent'; //these tests are run against an actual roku device. These cannot be enabled when run on the CI server describe('device', function device() { - let options: rokuDeploy.RokuDeployOptions; beforeEach(() => { fsExtra.emptyDirSync(tempDir); fsExtra.ensureDirSync(rootDir); process.chdir(rootDir); - options = rokuDeploy.getOptions({ - outDir: outDir, - host: '192.168.1.32', - retainDeploymentArchive: true, - password: 'aaaa', - devId: 'c6fdc2019903ac3332f624b0b2c2fe2c733c3e74', - rekeySignedPackage: `${cwd}/testSignedPackage.pkg`, - signingPassword: 'drRCEVWP/++K5TYnTtuAfQ==' - }); writeFiles(rootDir, [ ['manifest', undent` @@ -61,31 +49,4 @@ describe('device', function device() { }); this.timeout(20000); - - describe('deploy', () => { - it('works', async () => { - options.retainDeploymentArchive = true; - let response = await rokuDeploy.deploy(options); - assert.equal(response.message, 'Successful deploy'); - }); - - it('Presents nice message for 401 unauthorized status code', async () => { - this.timeout(20000); - options.password = 'NOT_THE_PASSWORD'; - await expectThrowsAsync( - rokuDeploy.deploy(options), - 'Unauthorized. Please verify username and password for target Roku.' - ); - }); - }); - - describe('deployAndSignPackage', () => { - it('works', async () => { - await rokuDeploy.deleteInstalledChannel(options); - await rokuDeploy.rekeyDevice(options); - expectPathExists( - await rokuDeploy.deployAndSignPackage(options) - ); - }); - }); }); diff --git a/src/index.ts b/src/index.ts index dff33ac..2fc0ec7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,61 +1,6 @@ -import { RokuDeploy } from './RokuDeploy'; - //export everything from the RokuDeploy file export * from './RokuDeploy'; export * from './util'; export * from './RokuDeployOptions'; export * from './Errors'; export * from './DeviceInfo'; - -//create a new static instance of RokuDeploy, and export those functions for backwards compatibility -export const rokuDeploy = new RokuDeploy(); - -let createPackage = RokuDeploy.prototype.createPackage.bind(rokuDeploy); -let deleteInstalledChannel = RokuDeploy.prototype.deleteInstalledChannel.bind(rokuDeploy); -let deploy = RokuDeploy.prototype.deploy.bind(rokuDeploy); -let deployAndSignPackage = RokuDeploy.prototype.deployAndSignPackage.bind(rokuDeploy); -let getDestPath = RokuDeploy.prototype.getDestPath.bind(rokuDeploy); -let getDeviceInfo = RokuDeploy.prototype.getDeviceInfo.bind(rokuDeploy); -let getFilePaths = RokuDeploy.prototype.getFilePaths.bind(rokuDeploy); -let getOptions = RokuDeploy.prototype.getOptions.bind(rokuDeploy); -let getOutputPkgFilePath = RokuDeploy.prototype.getOutputPkgFilePath.bind(rokuDeploy); -let getOutputZipFilePath = RokuDeploy.prototype.getOutputZipFilePath.bind(rokuDeploy); -let normalizeFilesArray = RokuDeploy.prototype.normalizeFilesArray.bind(rokuDeploy); -let normalizeRootDir = RokuDeploy.prototype.normalizeRootDir.bind(rokuDeploy); -let parseManifest = RokuDeploy.prototype.parseManifest.bind(rokuDeploy); -let prepublishToStaging = RokuDeploy.prototype.prepublishToStaging.bind(rokuDeploy); -let pressHomeButton = RokuDeploy.prototype.pressHomeButton.bind(rokuDeploy); -let publish = RokuDeploy.prototype.publish.bind(rokuDeploy); -let rekeyDevice = RokuDeploy.prototype.rekeyDevice.bind(rokuDeploy); -let retrieveSignedPackage = RokuDeploy.prototype.retrieveSignedPackage.bind(rokuDeploy); -let signExistingPackage = RokuDeploy.prototype.signExistingPackage.bind(rokuDeploy); -let stringifyManifest = RokuDeploy.prototype.stringifyManifest.bind(rokuDeploy); -let takeScreenshot = RokuDeploy.prototype.takeScreenshot.bind(rokuDeploy); -let zipFolder = RokuDeploy.prototype.zipFolder.bind(rokuDeploy); -let zipPackage = RokuDeploy.prototype.zipPackage.bind(rokuDeploy); - -export { - createPackage, - deleteInstalledChannel, - deploy, - deployAndSignPackage, - getDestPath, - getDeviceInfo, - getFilePaths, - getOptions, - getOutputPkgFilePath, - getOutputZipFilePath, - normalizeFilesArray, - normalizeRootDir, - parseManifest, - prepublishToStaging, - pressHomeButton, - publish, - rekeyDevice, - retrieveSignedPackage, - signExistingPackage, - stringifyManifest, - takeScreenshot, - zipFolder, - zipPackage -}; diff --git a/src/util.spec.ts b/src/util.spec.ts index 15c4fc9..f4047f7 100644 --- a/src/util.spec.ts +++ b/src/util.spec.ts @@ -1,7 +1,7 @@ import { util, standardizePath as s } from './util'; import { expect } from 'chai'; import * as fsExtra from 'fs-extra'; -import { tempDir } from './testUtils.spec'; +import { cwd, tempDir, rootDir } from './testUtils.spec'; import * as path from 'path'; import * as dns from 'dns'; import { createSandbox } from 'sinon'; @@ -14,6 +14,7 @@ describe('util', () => { }); afterEach(() => { + fsExtra.emptyDirSync(tempDir); sinon.restore(); }); @@ -26,14 +27,16 @@ describe('util', () => { }); }); - describe('toForwardSlashes', () => { - it('returns original value for non-strings', () => { - expect(util.toForwardSlashes(undefined)).to.be.undefined; - expect(util.toForwardSlashes(false)).to.be.false; + describe('standardizePathPosix', () => { + it('returns falsey value back unchanged', () => { + expect(util.standardizePathPosix(null)).to.eql(null); + expect(util.standardizePathPosix(undefined)).to.eql(undefined); + expect(util.standardizePathPosix(false as any)).to.eql(false); + expect(util.standardizePathPosix(0 as any)).to.eql(0); }); - it('converts mixed slashes to forward', () => { - expect(util.toForwardSlashes('a\\b/c\\d/e')).to.eql('a/b/c/d/e'); + it('always returns forward slashes', () => { + expect(util.standardizePathPosix('C:\\projects/some\\folder')).to.eql('C:/projects/some/folder'); }); }); @@ -107,10 +110,10 @@ describe('util', () => { }); describe('globAllByIndex', () => { - function writeFiles(filePaths: string[], cwd = tempDir) { + function writeFiles(filePaths: string[], dir = tempDir) { for (const filePath of filePaths) { fsExtra.outputFileSync( - path.resolve(cwd, filePath), + path.resolve(dir, filePath), '' ); } @@ -275,6 +278,15 @@ describe('util', () => { }); }); + describe('fileExistsCaseInsensitive', () => { + it('detects when a file does not exist inside a dir that does exist', async () => { + fsExtra.ensureDirSync(tempDir); + expect( + await util.fileExistsCaseInsensitive(s`${tempDir}/not-there`) + ).to.be.false; + }); + }); + describe('decodeHtmlEntities', () => { it('decodes values properly', () => { expect(util.decodeHtmlEntities(' ')).to.eql(' '); @@ -285,4 +297,233 @@ describe('util', () => { expect(util.decodeHtmlEntities(''')).to.eql(`'`); }); }); + + describe('objectToTableString', () => { + it('should print an object to a table', () => { + const deviceInfo = { + 'device-id': '1234', + 'serial-number': 'abcd' + }; + + const result = util.objectToTableString(deviceInfo); + + const expectedOutput = [ + 'Name Value ', + '---------------------------', + 'device-id 1234 ', + 'serial-number abcd ' + ].join('\n'); + + expect(result).to.eql(expectedOutput); + }); + + it('should still print a table when a value is null', () => { + const deviceInfo = { + 'device-id': '1234', + 'serial-number': null + }; + + const result = util.objectToTableString(deviceInfo); + + const expectedOutput = [ + 'Name Value ', + '---------------------------', + 'device-id 1234 ', + 'serial-number undefined' + ].join('\n'); + + expect(result).to.eql(expectedOutput); + }); + }); + + describe('normalizeRootDir', () => { + it('handles falsey values', () => { + expect(util.normalizeRootDir(null)).to.equal(cwd); + expect(util.normalizeRootDir(undefined)).to.equal(cwd); + expect(util.normalizeRootDir('')).to.equal(cwd); + expect(util.normalizeRootDir(' ')).to.equal(cwd); + expect(util.normalizeRootDir('\t')).to.equal(cwd); + }); + + it('handles non-falsey values', () => { + expect(util.normalizeRootDir(cwd)).to.equal(cwd); + expect(util.normalizeRootDir('./')).to.equal(cwd); + expect(util.normalizeRootDir('./testProject')).to.equal(path.join(cwd, 'testProject')); + }); + }); + + describe('getDestPath', () => { + it('handles unrelated exclusions properly', () => { + expect( + util.getDestPath( + s`${rootDir}/components/comp1/comp1.brs`, + [ + '**/*', + '!exclude.me' + ], + rootDir + ) + ).to.equal(s`components/comp1/comp1.brs`); + }); + + it('finds dest path for top-level path', () => { + expect( + util.getDestPath( + s`${rootDir}/components/comp1/comp1.brs`, + ['components/**/*'], + rootDir + ) + ).to.equal(s`components/comp1/comp1.brs`); + }); + + it('does not find dest path for non-matched top-level path', () => { + expect( + util.getDestPath( + s`${rootDir}/source/main.brs`, + ['components/**/*'], + rootDir + ) + ).to.be.undefined; + }); + + it('excludes a file that is negated', () => { + expect( + util.getDestPath( + s`${rootDir}/source/main.brs`, + [ + 'source/**/*', + '!source/main.brs' + ], + rootDir + ) + ).to.be.undefined; + }); + + it('excludes file from non-rootdir top-level pattern', () => { + expect( + util.getDestPath( + s`${rootDir}/../externalDir/source/main.brs`, + [ + '!../externalDir/**/*' + ], + rootDir + ) + ).to.be.undefined; + }); + + it('excludes a file that is negated in src;dest;', () => { + expect( + util.getDestPath( + s`${rootDir}/source/main.brs`, + [ + 'source/**/*', + { + src: '!source/main.brs' + } + ], + rootDir + ) + ).to.be.undefined; + }); + + it('works for brighterscript files', () => { + let destPath = util.getDestPath( + util.standardizePath(`${cwd}/src/source/main.bs`), + [ + 'manifest', + 'source/**/*.bs' + ], + s`${cwd}/src` + ); + expect(s`${destPath}`).to.equal(s`source/main.bs`); + }); + + it('excludes a file found outside the root dir', () => { + expect( + util.getDestPath( + s`${rootDir}/../source/main.brs`, + [ + '../source/**/*' + ], + rootDir + ) + ).to.be.undefined; + }); + }); + + describe('getOptionsFromJson', () => { + it('should fill in options from rokudeploy.json', () => { + fsExtra.outputJsonSync(s`${rootDir}/rokudeploy.json`, { password: 'password' }); + expect( + util.getOptionsFromJson({ cwd: rootDir }) + ).to.eql({ + password: 'password' + }); + }); + + it(`loads cwd from process`, () => { + try { + fsExtra.outputJsonSync(s`${process.cwd()}/rokudeploy.json`, { host: '1.2.3.4' }); + expect( + util.getOptionsFromJson() + ).to.eql({ + host: '1.2.3.4' + }); + } finally { + fsExtra.removeSync(s`${process.cwd()}/rokudeploy.json`); + } + }); + + it('catches invalid json with jsonc parser', () => { + fsExtra.writeJsonSync(s`${process.cwd()}/rokudeploy.json`, { host: '1.2.3.4' }); + sinon.stub(fsExtra, 'readFileSync').returns(` + { + "rootDir": "src" + `); + let ex; + try { + util.getOptionsFromJson(); + } catch (e) { + console.log(e); + ex = e; + } + expect(ex).to.exist; + expect(ex.message.startsWith('Error parsing')).to.be.true; + fsExtra.removeSync(s`${process.cwd()}/rokudeploy.json`); + }); + + it('works when loading stagingDir from rokudeploy.json', () => { + sinon.stub(fsExtra, 'existsSync').callsFake((filePath) => { + return true; + }); + sinon.stub(fsExtra, 'readFileSync').returns(` + { + "stagingDir": "./staging-dir" + } + `); + let options = util.getOptionsFromJson(); + expect(options.stagingDir.endsWith('staging-dir')).to.be.true; + }); + + it('supports jsonc for rokudeploy.json', () => { + fsExtra.writeFileSync(s`${tempDir}/rokudeploy.json`, ` + //leading comment + { + //inner comment + "rootDir": "src" //trailing comment + } + //trailing comment + `); + let options = util.getOptionsFromJson({ cwd: tempDir }); + expect(options.rootDir).to.equal('src'); + }); + }); + + describe('computeFileDestPath', () => { + it('treats {src;dest} without dest as a top-level string', () => { + expect( + util['computeFileDestPath'](s`${rootDir}/source/main.brs`, { src: s`source/main.brs` } as any, rootDir) + ).to.eql(s`source/main.brs`); + }); + }); }); diff --git a/src/util.ts b/src/util.ts index a0fe9f6..fe0aa89 100644 --- a/src/util.ts +++ b/src/util.ts @@ -5,6 +5,11 @@ import * as dns from 'dns'; import * as micromatch from 'micromatch'; // eslint-disable-next-line @typescript-eslint/no-require-imports import fastGlob = require('fast-glob'); +import type { FileEntry, RokuDeployOptions } from './RokuDeployOptions'; +import type { StandardizedFileEntry } from './RokuDeploy'; +import * as isGlob from 'is-glob'; +import * as picomatch from 'picomatch'; +import { parse as parseJsonc, printParseErrorCode, type ParseError } from 'jsonc-parser'; export class Util { /** @@ -46,22 +51,21 @@ export class Util { if (!thePath) { return thePath; } - return path.normalize( - thePath.replace(/[\/\\]+/g, path.sep) - ); + return path.normalize(thePath).replace(/[\/\\]+/g, path.sep); } /** - * Convert all slashes to forward slashes + * Normalize path and replace all directory separators with current OS separators + * @param thePath */ - public toForwardSlashes(thePath: string) { - if (typeof thePath === 'string') { - return thePath.replace(/[\/\\]+/g, '/'); - } else { + public standardizePathPosix(thePath: string) { + if (!thePath) { return thePath; } + return path.normalize(thePath).replace(/[\/\\]+/g, '/'); } + /** * Do a case-insensitive string replacement * @param subject the string that will have its contents replaced @@ -140,8 +144,6 @@ export class Util { cwd = cwd.replace(/\\/g, '/'); const globResults = patterns.map(async (pattern) => { - //force all windows-style slashes to unix style - pattern = pattern.replace(/\\/g, '/'); //skip negated patterns (we will use them to filter later on) if (pattern.startsWith('!')) { return pattern; @@ -228,6 +230,247 @@ export class Util { }); } + /** + * Given an array of `FilesType`, normalize them each into a `StandardizedFileEntry`. + * Each entry in the array or inner `src` array will be extracted out into its own object. + * This makes it easier to reason about later on in the process. + * @param files + */ + public normalizeFilesArray(files: FileEntry[]) { + const result: Array = []; + + for (let i = 0; i < files.length; i++) { + let entry = files[i]; + //skip falsey and blank entries + if (!entry) { + continue; + + //string entries + } else if (typeof entry === 'string') { + result.push(entry); + + //objects with src: (string | string[]) + } else if ('src' in entry) { + //validate dest + if (entry.dest !== undefined && entry.dest !== null && typeof entry.dest !== 'string') { + throw new Error(`Invalid type for "dest" at index ${i} of files array`); + } + + //objects with src: string + if (typeof entry.src === 'string') { + result.push({ + src: entry.src, + dest: util.standardizePath(entry.dest) + }); + + //objects with src:string[] + } else if ('src' in entry && Array.isArray(entry.src)) { + //create a distinct entry for each item in the src array + for (let srcEntry of entry.src) { + result.push({ + src: srcEntry, + dest: util.standardizePath(entry.dest) + }); + } + } else { + throw new Error(`Invalid type for "src" at index ${i} of files array`); + } + } else { + throw new Error(`Invalid entry at index ${i} in files array`); + } + } + + return result; + } + + /** + * Given a full path to a file, determine its dest path + * @param srcPath the absolute path to the file. This MUST be a file path, and it is not verified to exist on the filesystem + * @param files the files array + * @param rootDir the absolute path to the root dir + * @param skipMatch - skip running the minimatch process (i.e. assume the file is a match + * @returns the RELATIVE path to the dest location for the file. + */ + public getDestPath(srcPathAbsolute: string, files: FileEntry[], rootDir: string, skipMatch = false) { + srcPathAbsolute = util.standardizePath(srcPathAbsolute); + rootDir = rootDir.replace(/\\+/g, '/'); + const entries = util.normalizeFilesArray(files); + + function makeGlobAbsolute(pattern: string) { + return path.resolve( + path.posix.join( + rootDir, + //remove leading exclamation point if pattern is negated + pattern + //coerce all slashes to forward + ) + ).replace(/\\/g, '/'); + } + + let result: string; + + //add the file into every matching cache bucket + for (let entry of entries) { + const pattern = (typeof entry === 'string' ? entry : entry.src); + //filter previous paths + if (pattern.startsWith('!')) { + const keepFile = picomatch('!' + makeGlobAbsolute(pattern.replace(/^!/, ''))); + if (!keepFile(srcPathAbsolute)) { + result = undefined; + } + } else { + const keepFile = picomatch(makeGlobAbsolute(pattern)); + if (keepFile(srcPathAbsolute)) { + try { + result = this.computeFileDestPath( + srcPathAbsolute, + entry, + util.standardizePath(rootDir) + ); + } catch { + //ignore errors...the file just has no dest path + } + } + } + } + return result; + } + + /** + * Compute the `dest` path. This accounts for magic globstars in the pattern, + * as well as relative paths based on the dest. This is only used internally. + * @param src an absolute, normalized path for a file + * @param dest the `dest` entry for this file. If omitted, files will derive their paths relative to rootDir. + * @param pattern the glob pattern originally used to find this file + * @param rootDir absolute normalized path to the rootDir + */ + public computeFileDestPath(srcPath: string, entry: StandardizedFileEntry | string, rootDir: string) { + let result: string; + let globstarIdx: number; + //files under rootDir with no specified dest + if (typeof entry === 'string') { + if (util.isParentOfPath(rootDir, srcPath, false)) { + //files that are actually relative to rootDir + result = util.stringReplaceInsensitive(srcPath, rootDir, ''); + } else { + // result = util.stringReplaceInsensitive(srcPath, rootDir, ''); + throw new Error('Cannot reference a file outside of rootDir when using a top-level string. Please use a src;des; object instead'); + } + + //non-glob-pattern explicit file reference + } else if (!isGlob(entry.src.replace(/\\/g, '/'), { strict: false })) { + let isEntrySrcAbsolute = path.isAbsolute(entry.src); + let entrySrcPathAbsolute = isEntrySrcAbsolute ? entry.src : util.standardizePath(`${rootDir}/${entry.src}`); + + let isSrcChildOfRootDir = util.isParentOfPath(rootDir, entrySrcPathAbsolute, false); + + let fileNameAndExtension = path.basename(entrySrcPathAbsolute); + + //no dest + if (entry.dest === null || entry.dest === undefined) { + //no dest, absolute path or file outside of rootDir + if (isEntrySrcAbsolute || isSrcChildOfRootDir === false) { + //copy file to root of staging folder + result = fileNameAndExtension; + + //no dest, relative path, lives INSIDE rootDir + } else { + //copy relative file structure to root of staging folder + let srcPathRelative = util.stringReplaceInsensitive(entrySrcPathAbsolute, rootDir, ''); + result = srcPathRelative; + } + + //assume entry.dest is the relative path to the folder AND file if applicable + } else if (entry.dest === '') { + result = fileNameAndExtension; + } else { + result = entry.dest; + } + //has a globstar + } else if ((globstarIdx = entry.src.indexOf('**')) > -1) { + const rootGlobstarPath = path.resolve(rootDir, entry.src.substring(0, globstarIdx)) + path.sep; + const srcPathRelative = util.stringReplaceInsensitive(srcPath, rootGlobstarPath, ''); + if (entry.dest) { + result = `${entry.dest}/${srcPathRelative}`; + } else { + result = srcPathRelative; + } + + //`pattern` is some other glob magic + } else { + const fileNameAndExtension = path.basename(srcPath); + if (entry.dest) { + result = util.standardizePath(`${entry.dest}/${fileNameAndExtension}`); + } else { + result = util.stringReplaceInsensitive(srcPath, rootDir, ''); + } + } + + result = util.standardizePath( + //remove leading slashes + result.replace(/^[\/\\]+/, '') + ); + return result; + } + + /** + * Given a root directory, normalize it to a full path. + * Fall back to cwd if not specified + * @param rootDir + */ + public normalizeRootDir(rootDir: string) { + if (!rootDir || (typeof rootDir === 'string' && rootDir.trim().length === 0)) { + return process.cwd(); + } else { + return path.resolve(rootDir); + } + } + + public objectToTableString(deviceInfo: Record) { + const margin = 5; + const keyWidth = Math.max(...Object.keys(deviceInfo).map(x => x.length)) + margin; + const valueWidth = Math.max(...Object.values(deviceInfo).map(x => (x ?? '').toString().length)) + margin; + let table = []; + table.push('Name'.padEnd(keyWidth, ' ') + 'Value'.padEnd(keyWidth, ' ')); + table.push('-'.repeat(keyWidth + valueWidth)); + for (const [key, value] of Object.entries(deviceInfo)) { + table.push(key.padEnd(keyWidth, ' ') + value?.toString().padEnd(keyWidth, ' ')); + } + + return table.join('\n'); + } + + /** + * A function to fill in any missing arguments with JSON values + * Only run when CLI commands are used + */ + public getOptionsFromJson(options?: { cwd?: string; configPath?: string }) { + let fileOptions: RokuDeployOptions = {}; + const cwd = options?.cwd ?? process.cwd(); + const configPath = options?.configPath ?? path.join(cwd, 'rokudeploy.json'); + + if (fsExtra.existsSync(configPath)) { + let configFileText = fsExtra.readFileSync(configPath).toString(); + let parseErrors = [] as ParseError[]; + fileOptions = parseJsonc(configFileText, parseErrors, { + allowEmptyContent: true, + allowTrailingComma: true, + disallowComments: false + }); + if (parseErrors.length > 0) { + throw new Error(`Error parsing "${path.resolve(configPath)}": ` + JSON.stringify( + parseErrors.map(x => { + return { + message: printParseErrorCode(x.error), + offset: x.offset, + length: x.length + }; + }) + )); + } + } + return fileOptions; + } } export let util = new Util(); @@ -245,3 +488,16 @@ export function standardizePath(stringParts, ...expressions: any[]) { result.join('') ); } + +/** + * A tagged template literal function for standardizing the path and making all path separators forward slashes + */ +export function standardizePathPosix(stringParts, ...expressions: any[]) { + let result = []; + for (let i = 0; i < stringParts.length; i++) { + result.push(stringParts[i], expressions[i]); + } + return util.standardizePathPosix( + result.join('') + ); +} diff --git a/tsconfig.json b/tsconfig.json index 381ba26..5d1cd50 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,7 +21,7 @@ "include": [ "src/**/*.ts", "device.spec.ts" - ], +, "src/cli.ts" ], "ts-node": { "transpileOnly": true }